Hi, It''s been a while, but after a looooooooooooong time we''ve (hopefully) finally sorted the merging issues with qemu-xen. So here is a fresh version of the xen support for qemu patch series. Short overview (individual patches have longer descriptions): #1 - groundwork for xen support (makefiles, configure, ...). #2 - backend driver core (common code used by all backends). #3 - add console backend driver. #4 - add framebuffer backend driver. With these four patches in place upstream qemu is functional aequivalent to qemu-xen for paravirtual guests. The patches are merged into qemu-xen already, with the exception of a few framebuffer bits which got hold back due to displaystate work. #5 - add block device backend driver. #6 - add net backend driver. These two patches add backend drivers for disk and network to qemu. #7 - blk & nic configuration via cmd line. #8 - pv domain builder. #9 - simplify vga selection #10 - add -vga xenfb option, configure xenfb These patches add support to qemu for creating xen pv guests. That way one can run xen guests without xend. Patch #8 is the domain builder code itself. The other patches add support for configuring devices (fb, disk, nic) via command line. cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-01 21:39 UTC
[Xen-devel] [PATCH 01/10] xen: groundwork for xen support
- configure script and build system changes. - wind up new machine type. - add -xen-* command line options. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 8 +++++ configure | 34 +++++++++++++++++++++++ hw/boards.h | 3 ++ hw/xen.h | 20 ++++++++++++++ hw/xen_machine_pv.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ qemu-options.hx | 11 +++++++ target-i386/machine.c | 3 ++ vl.c | 12 ++++++++ 8 files changed, 161 insertions(+), 0 deletions(-) create mode 100644 hw/xen.h create mode 100644 hw/xen_machine_pv.c diff --git a/Makefile.target b/Makefile.target index 046427d..1da8055 100644 --- a/Makefile.target +++ b/Makefile.target @@ -563,6 +563,14 @@ ifdef CONFIG_BLUEZ LIBS += $(CONFIG_BLUEZ_LIBS) endif +# xen backend driver support +XEN_OBJS := xen_machine_pv.o +ifeq ($(CONFIG_XEN), yes) + OBJS += $(XEN_OBJS) + LIBS += $(XEN_LIBS) + $(XEN_OBJS) : CFLAGS += -Wall -Wmissing-prototypes -Wstrict-prototypes +endif + # SCSI layer OBJS+= lsi53c895a.o esp.o diff --git a/configure b/configure index 5c62c59..212f2e9 100755 --- a/configure +++ b/configure @@ -189,6 +189,7 @@ aix="no" blobs="yes" fdt="yes" sdl_x11="no" +xen="yes" # OS specific if check_define __linux__ ; then @@ -406,6 +407,8 @@ for opt do ;; --disable-kqemu) kqemu="no" ;; + --disable-xen) xen="no" + ;; --disable-brlapi) brlapi="no" ;; --disable-bluez) bluez="no" @@ -555,6 +558,7 @@ echo " Available drivers: $audio_possible_drivers" echo " --audio-card-list=LIST set list of emulated audio cards [$audio_card_list]" echo " Available cards: $audio_possible_cards" echo " --enable-mixemu enable mixer emulation" +echo " --disable-xen disable xen backend driver support" echo " --disable-brlapi disable BrlAPI" echo " --disable-vnc-tls disable TLS encryption for VNC server" echo " --disable-vnc-sasl disable SASL encryption for VNC server" @@ -768,6 +772,22 @@ else fi ########################################## +# xen probe + +if test "$xen" = "yes" ; then +cat > $TMPC <<EOF +#include <xenctrl.h> +#include <xs.h> +int main(void) { xs_daemon_open; xc_interface_open; } +EOF + if $cc $ARCH_CFLAGS -c -o $TMPO $TMPC -lxenstore -lxenctrl 2> /dev/null ; then + : + else + xen="no" + fi +fi + +########################################## # SDL probe sdl_too_old=no @@ -1197,6 +1217,7 @@ if test -n "$sparc_cpu"; then echo "Target Sparc Arch $sparc_cpu" fi echo "kqemu support $kqemu" +echo "xen support $xen" echo "brlapi support $brlapi" echo "Documentation $build_docs" [ ! -z "$uname_release" ] && \ @@ -1496,6 +1517,9 @@ if test "$bluez" = "yes" ; then echo "CONFIG_BLUEZ_LIBS=$bluez_libs" >> $config_mak echo "#define CONFIG_BLUEZ 1" >> $config_h fi +if test "$xen" = "yes" ; then + echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak +fi if test "$aio" = "yes" ; then echo "#define CONFIG_AIO 1" >> $config_h echo "CONFIG_AIO=yes" >> $config_mak @@ -1657,6 +1681,11 @@ case "$target_cpu" in echo "KVM_CFLAGS=$kvm_cflags" >> $config_mak echo "#define CONFIG_KVM 1" >> $config_h fi + if test "$xen" = "yes" -a "$target_softmmu" = "yes"; + then + echo "CONFIG_XEN=yes" >> $config_mak + echo "#define CONFIG_XEN 1" >> $config_h + fi ;; x86_64) echo "TARGET_ARCH=x86_64" >> $config_mak @@ -1672,6 +1701,11 @@ case "$target_cpu" in echo "KVM_CFLAGS=$kvm_cflags" >> $config_mak echo "#define CONFIG_KVM 1" >> $config_h fi + if test "$xen" = "yes" -a "$target_softmmu" = "yes" + then + echo "CONFIG_XEN=yes" >> $config_mak + echo "#define CONFIG_XEN 1" >> $config_h + fi ;; alpha) echo "TARGET_ARCH=alpha" >> $config_mak diff --git a/hw/boards.h b/hw/boards.h index 1e18ba6..215ccdc 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -34,6 +34,9 @@ extern QEMUMachine axisdev88_machine; extern QEMUMachine pc_machine; extern QEMUMachine isapc_machine; +/* xen_machine.c */ +extern QEMUMachine xenpv_machine; + /* ppc.c */ extern QEMUMachine prep_machine; extern QEMUMachine core99_machine; diff --git a/hw/xen.h b/hw/xen.h new file mode 100644 index 0000000..3c8da41 --- /dev/null +++ b/hw/xen.h @@ -0,0 +1,20 @@ +#ifndef QEMU_HW_XEN_H +#define QEMU_HW_XEN_H 1 +/* + * public xen header + * stuff needed outside xen-*.c, i.e. interfaces to qemu. + * must not depend on any xen headers being present in + * /usr/include/xen, so it can be included unconditionally. + */ + +/* xen-machine.c */ +enum xen_mode { + XEN_EMULATE = 0, // xen emulation, using xenner (default) + XEN_CREATE, // create xen domain + XEN_ATTACH // attach to xen domain created by xend +}; + +extern uint32_t xen_domid; +extern enum xen_mode xen_mode; + +#endif /* QEMU_HW_XEN_H */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c new file mode 100644 index 0000000..21b5d9e --- /dev/null +++ b/hw/xen_machine_pv.c @@ -0,0 +1,70 @@ +/* + * QEMU Xen PV Machine + * + * Copyright (c) 2007 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw.h" +#include "pc.h" +#include "sysemu.h" +#include "boards.h" +#include "xen.h" + +uint32_t xen_domid; +enum xen_mode xen_mode = XEN_EMULATE; + +static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + CPUState *env; + + /* Initialize a dummy CPU */ + if (cpu_model == NULL) { +#ifdef TARGET_X86_64 + cpu_model = "qemu64"; +#else + cpu_model = "qemu32"; +#endif + } + env = cpu_init(cpu_model); + env->halted = 1; +} + +QEMUMachine xenpv_machine = { + .name = "xenpv", + .desc = "Xen Para-virtualized PC", + .init = xen_init_pv, + .ram_require = (4 * 1024 * 1024) | RAMSIZE_FIXED, + .max_cpus = 1, +}; + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/qemu-options.hx b/qemu-options.hx index 6c58e2a..895fa89 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1294,6 +1294,17 @@ Enable KVM full virtualization support. This option is only available if KVM support is enabled when compiling. ETEXI +#ifdef CONFIG_XEN +DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid, + "-xen-domid id specify xen guest domain id\n") +DEF("xen-create", 0, QEMU_OPTION_xen_create, + "-xen-create create domain using xen hypercalls, bypassing xend\n" + " warning: should not be used when xend is in use\n") +DEF("xen-attach", 0, QEMU_OPTION_xen_attach, + "-xen-attach attach to existing xen domain\n" + " xend will use this when starting qemu\n") +#endif + DEF("no-reboot", 0, QEMU_OPTION_no_reboot, \ "-no-reboot exit instead of rebooting\n") STEXI diff --git a/target-i386/machine.c b/target-i386/machine.c index 1cf49d5..1b0d36d 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -9,6 +9,9 @@ void register_machines(void) { qemu_register_machine(&pc_machine); qemu_register_machine(&isapc_machine); +#ifdef CONFIG_XEN + qemu_register_machine(&xenpv_machine); +#endif } static void cpu_put_seg(QEMUFile *f, SegmentCache *dt) diff --git a/vl.c b/vl.c index 5e6c621..76fca5a 100644 --- a/vl.c +++ b/vl.c @@ -138,6 +138,7 @@ int main(int argc, char **argv) #include "hw/isa.h" #include "hw/baum.h" #include "hw/bt.h" +#include "hw/xen.h" #include "bt-host.h" #include "net.h" #include "monitor.h" @@ -4943,6 +4944,17 @@ int main(int argc, char **argv, char **envp) run_as = optarg; break; #endif +#ifdef CONFIG_XEN + case QEMU_OPTION_xen_domid: + xen_domid = atoi(optarg); + break; + case QEMU_OPTION_xen_create: + xen_mode = XEN_CREATE; + break; + case QEMU_OPTION_xen_attach: + xen_mode = XEN_ATTACH; + break; +#endif } } } -- 1.6.1.3 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
This patch adds infrastructure for xen backend drivers living in qemu, so drivers don''t need to implement common stuff on their own. It''s mostly xenbus management stuff: some functions to access xentore, setting up xenstore watches, callbacks on device discovery and state changes, handle event channel, ... Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen_backend.c | 691 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_backend.h | 86 +++++++ hw/xen_common.h | 34 +++ hw/xen_machine_pv.c | 8 +- 5 files changed, 819 insertions(+), 2 deletions(-) create mode 100644 hw/xen_backend.c create mode 100644 hw/xen_backend.h create mode 100644 hw/xen_common.h diff --git a/Makefile.target b/Makefile.target index 1da8055..dd8c0cb 100644 --- a/Makefile.target +++ b/Makefile.target @@ -564,7 +564,7 @@ LIBS += $(CONFIG_BLUEZ_LIBS) endif # xen backend driver support -XEN_OBJS := xen_machine_pv.o +XEN_OBJS := xen_machine_pv.o xen_backend.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.c b/hw/xen_backend.c new file mode 100644 index 0000000..2678dfa --- /dev/null +++ b/hw/xen_backend.c @@ -0,0 +1,691 @@ +/* + * xen backend driver infrastructure + * (c) 2008 Gerd Hoffmann <kraxel@redhat.com> + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * TODO: add some xenbus / xenstore concepts overview here. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/signal.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/grant_table.h> + +#include "hw.h" +#include "qemu-char.h" +#include "xen_backend.h" + +/* ------------------------------------------------------------- */ + +/* public */ +int xen_xc; +struct xs_handle *xenstore = NULL; + +/* private */ +static TAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = TAILQ_HEAD_INITIALIZER(xendevs); +static int debug = 0; + +/* ------------------------------------------------------------- */ + +int xenstore_write_str(const char *base, const char *node, const char *val) +{ + char abspath[XEN_BUFSIZE]; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + if (!xs_write(xenstore, 0, abspath, val, strlen(val))) + return -1; + return 0; +} + +char *xenstore_read_str(const char *base, const char *node) +{ + char abspath[XEN_BUFSIZE]; + unsigned int len; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + return xs_read(xenstore, 0, abspath, &len); +} + +int xenstore_write_int(const char *base, const char *node, int ival) +{ + char val[32]; + + snprintf(val, sizeof(val), "%d", ival); + return xenstore_write_str(base, node, val); +} + +int xenstore_read_int(const char *base, const char *node, int *ival) +{ + char *val; + int rc = -1; + + val = xenstore_read_str(base, node); + if (val && 1 == sscanf(val, "%d", ival)) + rc = 0; + qemu_free(val); + return rc; +} + +int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val) +{ + return xenstore_write_str(xendev->be, node, val); +} + +int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival) +{ + return xenstore_write_int(xendev->be, node, ival); +} + +char *xenstore_read_be_str(struct XenDevice *xendev, const char *node) +{ + return xenstore_read_str(xendev->be, node); +} + +int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival) +{ + return xenstore_read_int(xendev->be, node, ival); +} + +char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node) +{ + return xenstore_read_str(xendev->fe, node); +} + +int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival) +{ + return xenstore_read_int(xendev->fe, node, ival); +} + +/* ------------------------------------------------------------- */ + +const char *xenbus_strstate(enum xenbus_state state) +{ + static const char *const name[] = { + [ XenbusStateUnknown ] = "Unknown", + [ XenbusStateInitialising ] = "Initialising", + [ XenbusStateInitWait ] = "InitWait", + [ XenbusStateInitialised ] = "Initialised", + [ XenbusStateConnected ] = "Connected", + [ XenbusStateClosing ] = "Closing", + [ XenbusStateClosed ] = "Closed", + }; + return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; +} + +int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) +{ + int rc; + + rc = xenstore_write_be_int(xendev, "state", state); + if (rc < 0) + return rc; + xen_be_printf(xendev, 1, "backend state: %s -> %s\n", + xenbus_strstate(xendev->be_state), xenbus_strstate(state)); + xendev->be_state = state; + return 0; +} + +/* ------------------------------------------------------------- */ + +struct XenDevice *xen_be_find_xendev(const char *type, int dom, int dev) +{ + struct XenDevice *xendev; + + TAILQ_FOREACH(xendev, &xendevs, next) { + if (xendev->dom != dom) + continue; + if (xendev->dev != dev) + continue; + if (0 != strcmp(xendev->type, type)) + continue; + return xendev; + } + return NULL; +} + +/* + * get xen backend device, allocate a new one if it doesn''t exist. + */ +static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, + struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char *dom0; + + xendev = xen_be_find_xendev(type, dom, dev); + if (xendev) + return xendev; + + /* init new xendev */ + xendev = qemu_mallocz(ops->size); + if (!xendev) + return NULL; + xendev->type = type; + xendev->dom = dom; + xendev->dev = dev; + xendev->ops = ops; + + dom0 = xs_get_domain_path(xenstore, 0); + snprintf(xendev->be, sizeof(xendev->be), "%s/backend/%s/%d/%d", + dom0, xendev->type, xendev->dom, xendev->dev); + snprintf(xendev->name, sizeof(xendev->name), "%s-%d", + xendev->type, xendev->dev); + free(dom0); + + xendev->debug = debug; + xendev->local_port = -1; + + xendev->evtchndev = xc_evtchn_open(); + if (xendev->evtchndev < 0) { + fprintf(stderr, "can''t open evtchn device\n"); + qemu_free(xendev); + return NULL; + } + fcntl(xc_evtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC); + + if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { + xendev->gnttabdev = xc_gnttab_open(); + if (xendev->gnttabdev < 0) { + fprintf(stderr, "can''t open gnttab device\n"); + xc_evtchn_close(xendev->evtchndev); + qemu_free(xendev); + return NULL; + } + } else { + xendev->gnttabdev = -1; + } + + TAILQ_INSERT_TAIL(&xendevs, xendev, next); + + if (xendev->ops->alloc) + xendev->ops->alloc(xendev); + + return xendev; +} + +/* + * release xen backend device. + */ +static struct XenDevice *xen_be_del_xendev(int dom, int dev) +{ + struct XenDevice *xendev, *xnext; + + /* + * This is pretty much like TAILQ_FOREACH(xendev, &xendevs, next) but + * we save the next pointer in xnext because we might free xendev. + */ + xnext = xendevs.tqh_first; + while (xnext) { + xendev = xnext; + xnext = xendev->next.tqe_next; + + if (xendev->dom != dom) + continue; + if (xendev->dev != dev && dev != -1) + continue; + + if (xendev->ops->free) + xendev->ops->free(xendev); + + if (xendev->fe) { + char token[XEN_BUFSIZE]; + snprintf(token, sizeof(token), "fe:%p", xendev); + xs_unwatch(xenstore, xendev->fe, token); + qemu_free(xendev->fe); + } + + if (xendev->evtchndev >= 0) + xc_evtchn_close(xendev->evtchndev); + if (xendev->gnttabdev >= 0) + xc_gnttab_close(xendev->gnttabdev); + + TAILQ_REMOVE(&xendevs, xendev, next); + qemu_free(xendev); + } + return NULL; +} + +/* + * Sync internal data structures on xenstore updates. + * Node specifies the changed field. node = NULL means + * update all fields (used for initialization). + */ +static void xen_be_backend_changed(struct XenDevice *xendev, const char *node) +{ + if (NULL == node || 0 == strcmp(node, "online")) { + if (-1 == xenstore_read_be_int(xendev, "online", &xendev->online)) + xendev->online = 0; + } + + if (node) { + xen_be_printf(xendev, 2, "backend update: %s\n", node); + if (xendev->ops->backend_changed) + xendev->ops->backend_changed(xendev, node); + } +} + +static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node) +{ + int fe_state; + + if (NULL == node || 0 == strcmp(node, "state")) { + if (-1 == xenstore_read_fe_int(xendev, "state", &fe_state)) + fe_state = XenbusStateUnknown; + if (xendev->fe_state != fe_state) + xen_be_printf(xendev, 1, "frontend state: %s -> %s\n", + xenbus_strstate(xendev->fe_state), + xenbus_strstate(fe_state)); + xendev->fe_state = fe_state; + } + if (NULL == node || 0 == strcmp(node, "protocol")) { + qemu_free(xendev->protocol); + xendev->protocol = xenstore_read_fe_str(xendev, "protocol"); + if (xendev->protocol) + xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol); + } + + if (node) { + xen_be_printf(xendev, 2, "frontend update: %s\n", node); + if (xendev->ops->frontend_changed) + xendev->ops->frontend_changed(xendev, node); + } +} + +/* ------------------------------------------------------------- */ +/* Check for possible state transitions and perform them. */ + +/* + * Initial xendev setup. Read frontend path, register watch for it. + * Should succeed once xend finished setting up the backend device. + * + * Also sets initial state (-> Initializing) when done. Which + * only affects the xendev->be_state variable as xenbus should + * already be put into that state by xend. + */ +static int xen_be_try_setup(struct XenDevice *xendev) +{ + char token[XEN_BUFSIZE]; + int be_state; + + if (-1 == xenstore_read_be_int(xendev, "state", &be_state)) { + xen_be_printf(xendev, 0, "reading backend state failed\n"); + return -1; + } + + if (be_state != XenbusStateInitialising) { + xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n", + xenbus_strstate(be_state)); + return -1; + } + + xendev->fe = xenstore_read_be_str(xendev, "frontend"); + if (NULL == xendev->fe) { + xen_be_printf(xendev, 0, "reading frontend path failed\n"); + return -1; + } + + /* setup frontend watch */ + snprintf(token, sizeof(token), "fe:%p", xendev); + if (!xs_watch(xenstore, xendev->fe, token)) { + xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n", + xendev->fe); + return -1; + } + xen_be_set_state(xendev, XenbusStateInitialising); + + xen_be_backend_changed(xendev, NULL); + xen_be_frontend_changed(xendev, NULL); + return 0; +} + +/* + * Try initialize xendev. Prepare everything the backend can do + * without synchronizing with the frontend. Fakes hotplug-status. No + * hotplug involved here because this is about userspace drivers, thus + * there are kernel backend devices which could invoke hotplug. + * + * Goes to InitWait on success. + */ +static int xen_be_try_init(struct XenDevice *xendev) +{ + int rc = 0; + + if (!xendev->online) { + xen_be_printf(xendev, 1, "not online\n"); + return -1; + } + + if (xendev->ops->init) + rc = xendev->ops->init(xendev); + if (0 != rc) { + xen_be_printf(xendev, 1, "init() failed\n"); + return rc; + } + + xenstore_write_be_str(xendev, "hotplug-status", "connected"); + xen_be_set_state(xendev, XenbusStateInitWait); + return 0; +} + +/* + * Try to connect xendev. Depends on the frontend being ready + * for it (shared ring and evtchn info in xenstore, state being + * Initialised or Connected). + * + * Goes to Connected on success. + */ +static int xen_be_try_connect(struct XenDevice *xendev) +{ + int rc = 0; + + if (xendev->fe_state != XenbusStateInitialised && + xendev->fe_state != XenbusStateConnected) { + if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { + xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); + } else { + xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); + return -1; + } + } + + if (xendev->ops->connect) + rc = xendev->ops->connect(xendev); + if (0 != rc) { + xen_be_printf(xendev, 0, "connect() failed\n"); + return rc; + } + + xen_be_set_state(xendev, XenbusStateConnected); + return 0; +} + +/* + * Teardown connection. + * + * Goes to Closed when done. + */ +static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state) +{ + if (xendev->be_state != XenbusStateClosing && + xendev->be_state != XenbusStateClosed && + xendev->ops->disconnect) + xendev->ops->disconnect(xendev); + if (xendev->be_state != state) + xen_be_set_state(xendev, state); +} + +/* + * Try to reset xendev, for reconnection by another frontend instance. + */ +static int xen_be_try_reset(struct XenDevice *xendev) +{ + if (xendev->fe_state != XenbusStateInitialising) + return -1; + + xen_be_printf(xendev, 1, "device reset (for re-connect)\n"); + xen_be_set_state(xendev, XenbusStateInitialising); + return 0; +} + +/* + * state change dispatcher function + */ +void xen_be_check_state(struct XenDevice *xendev) +{ + int rc = 0; + + /* frontend may request shutdown from almost anywhere */ + if (xendev->fe_state == XenbusStateClosing || + xendev->fe_state == XenbusStateClosed) { + xen_be_disconnect(xendev, xendev->fe_state); + return; + } + + /* check for possible backend state transitions */ + for (;;) { + switch (xendev->be_state) { + case XenbusStateUnknown: + rc = xen_be_try_setup(xendev); + break; + case XenbusStateInitialising: + rc = xen_be_try_init(xendev); + break; + case XenbusStateInitWait: + rc = xen_be_try_connect(xendev); + break; + case XenbusStateClosed: + rc = xen_be_try_reset(xendev); + break; + default: + rc = -1; + } + if (0 != rc) + break; + } +} + +/* ------------------------------------------------------------- */ + +static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char path[XEN_BUFSIZE], token[XEN_BUFSIZE]; + char **dev = NULL, *dom0; + unsigned int cdev, j; + + /* setup watch */ + dom0 = xs_get_domain_path(xenstore, 0); + snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops); + snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom); + free(dom0); + if (!xs_watch(xenstore, path, token)) { + fprintf(stderr, "xen be: watching backend path (%s) failed\n", path); + return -1; + } + + /* look for backends */ + dev = xs_directory(xenstore, 0, path, &cdev); + if (!dev) + return 0; + for (j = 0; j < cdev; j++) { + xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops); + if (NULL == xendev) + continue; + xen_be_check_state(xendev); + } + qemu_free(dev); + return 0; +} + +static void xenstore_update_be(char *watch, char *type, int dom, + struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char path[XEN_BUFSIZE], *dom0; + unsigned int len, dev; + + dom0 = xs_get_domain_path(xenstore, 0); + len = snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom); + free(dom0); + if (0 != strncmp(path, watch, len)) + return; + if (2 != sscanf(watch+len, "/%u/%255s", &dev, path)) { + strcpy(path, ""); + if (1 != sscanf(watch+len, "/%u", &dev)) + dev = -1; + } + if (-1 == dev) + return; + + if (0) { + /* FIXME: detect devices being deleted from xenstore ... */ + xen_be_del_xendev(dom, dev); + } + + xendev = xen_be_get_xendev(type, dom, dev, ops); + if (NULL != xendev) { + xen_be_backend_changed(xendev, path); + xen_be_check_state(xendev); + } +} + +static void xenstore_update_fe(char *watch, struct XenDevice *xendev) +{ + char *node; + unsigned int len; + + len = strlen(xendev->fe); + if (0 != strncmp(xendev->fe, watch, len)) + return; + if (watch[len] != ''/'') + return; + node = watch + len + 1; + + xen_be_frontend_changed(xendev, node); + xen_be_check_state(xendev); +} + +static void xenstore_update(void *unused) +{ + char **vec = NULL; + intptr_t type, ops, ptr; + unsigned int dom, count; + + vec = xs_read_watch(xenstore, &count); + if (NULL == vec) + goto cleanup; + + if (3 == sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR, + &type, &dom, &ops)) + xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops); + if (1 == sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr)) + xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr); + +cleanup: + qemu_free(vec); +} + +static void xen_be_evtchn_event(void *opaque) +{ + struct XenDevice *xendev = opaque; + evtchn_port_t port; + + port = xc_evtchn_pending(xendev->evtchndev); + if (port != xendev->local_port) { + xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n", + port, xendev->local_port); + return; + } + xc_evtchn_unmask(xendev->evtchndev, port); + + if (xendev->ops->event) + xendev->ops->event(xendev); +} + +/* -------------------------------------------------------------------- */ + +int xen_be_init(void) +{ + xenstore = xs_daemon_open(); + if (!xenstore) { + fprintf(stderr, "can''t connect to xenstored\n"); + return -1; + } + + if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) + goto err; + + xen_xc = xc_interface_open(); + if (-1 == xen_xc) { + fprintf(stderr, "can''t open xen interface\n"); + goto err; + } + return 0; + +err: + qemu_set_fd_handler(xs_fileno(xenstore), NULL, NULL, NULL); + xs_daemon_close(xenstore); + xenstore = NULL; + + return -1; +} + +int xen_be_register(const char *type, struct XenDevOps *ops) +{ + return xenstore_scan(type, xen_domid, ops); +} + +int xen_be_bind_evtchn(struct XenDevice *xendev) +{ + if (xendev->local_port != -1) + return 0; + xendev->local_port = xc_evtchn_bind_interdomain + (xendev->evtchndev, xendev->dom, xendev->remote_port); + if (-1 == xendev->local_port) { + xen_be_printf(xendev, 0, "xc_evtchn_bind_interdomain failed\n"); + return -1; + } + xen_be_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), + xen_be_evtchn_event, NULL, xendev); + return 0; +} + +void xen_be_unbind_evtchn(struct XenDevice *xendev) +{ + if (xendev->local_port == -1) + return; + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL); + xc_evtchn_unbind(xendev->evtchndev, xendev->local_port); + xen_be_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port); + xendev->local_port = -1; +} + +int xen_be_send_notify(struct XenDevice *xendev) +{ + return xc_evtchn_notify(xendev->evtchndev, xendev->local_port); +} + +/* + * msg_level: + * 0 == errors. + * 1 == informative debug messages. + * 2 == noisy debug messages. + * 3 == will flood your log. + */ +void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) +{ + va_list args; + + if (msg_level > xendev->debug) + return; + fprintf(stderr, "xen be: %s: ", xendev->name); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} diff --git a/hw/xen_backend.h b/hw/xen_backend.h new file mode 100644 index 0000000..a1243f6 --- /dev/null +++ b/hw/xen_backend.h @@ -0,0 +1,86 @@ +#ifndef QEMU_HW_XEN_BACKEND_H +#define QEMU_HW_XEN_BACKEND_H 1 + +#include "xen_common.h" + +/* ------------------------------------------------------------- */ + +#define XEN_BUFSIZE 1024 + +struct XenDevice; + +/* driver uses grant tables -> open gntdev device (xendev->gnttabdev) */ +#define DEVOPS_FLAG_NEED_GNTDEV 1 +/* don''t expect frontend doing correct state transitions (aka console quirk) */ +#define DEVOPS_FLAG_IGNORE_STATE 2 + +struct XenDevOps { + size_t size; + uint32_t flags; + void (*alloc)(struct XenDevice *xendev); + int (*init)(struct XenDevice *xendev); + int (*connect)(struct XenDevice *xendev); + void (*event)(struct XenDevice *xendev); + void (*disconnect)(struct XenDevice *xendev); + int (*free)(struct XenDevice *xendev); + void (*backend_changed)(struct XenDevice *xendev, const char *node); + void (*frontend_changed)(struct XenDevice *xendev, const char *node); +}; + +struct XenDevice { + const char *type; + int dom; + int dev; + char name[64]; + int debug; + + enum xenbus_state be_state; + enum xenbus_state fe_state; + int online; + char be[XEN_BUFSIZE]; + char *fe; + char *protocol; + int remote_port; + int local_port; + + int evtchndev; + int gnttabdev; + + struct XenDevOps *ops; + TAILQ_ENTRY(XenDevice) next; +}; + +/* ------------------------------------------------------------- */ + +/* variables */ +extern int xen_xc; +extern struct xs_handle *xenstore; + +/* xenstore helper functions */ +int xenstore_write_str(const char *base, const char *node, const char *val); +int xenstore_write_int(const char *base, const char *node, int ival); +char *xenstore_read_str(const char *base, const char *node); +int xenstore_read_int(const char *base, const char *node, int *ival); + +int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val); +int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival); +char *xenstore_read_be_str(struct XenDevice *xendev, const char *node); +int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival); +char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node); +int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival); + +const char *xenbus_strstate(enum xenbus_state state); +struct XenDevice *xen_be_find_xendev(const char *type, int dom, int dev); +void xen_be_check_state(struct XenDevice *xendev); + +/* xen backend driver bits */ +int xen_be_init(void); +int xen_be_register(const char *type, struct XenDevOps *ops); +int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state); +int xen_be_bind_evtchn(struct XenDevice *xendev); +void xen_be_unbind_evtchn(struct XenDevice *xendev); +int xen_be_send_notify(struct XenDevice *xendev); +void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) + __attribute__ ((format(printf, 3, 4))); + +#endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_common.h b/hw/xen_common.h new file mode 100644 index 0000000..7562567 --- /dev/null +++ b/hw/xen_common.h @@ -0,0 +1,34 @@ +#ifndef QEMU_HW_XEN_COMMON_H +#define QEMU_HW_XEN_COMMON_H 1 + +#include <stddef.h> +#include <inttypes.h> + +#include <xenctrl.h> +#include <xs.h> +#include <xen/io/xenbus.h> + +#include "hw.h" +#include "xen.h" +#include "sys-queue.h" /* BSD list implementation */ + +/* + * tweaks needed to build with different xen versions + * 0x00030205 -> 3.1.0 + * 0x00030207 -> 3.2.0 + * 0x00030208 -> unstable + */ +#include <xen/xen-compat.h> +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030205 +# define evtchn_port_or_error_t int +#endif +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030207 +# define xc_map_foreign_pages xc_map_foreign_batch +#endif +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030208 +# define xen_mb() mb() +# define xen_rmb() rmb() +# define xen_wmb() wmb() +#endif + +#endif /* QEMU_HW_XEN_COMMON_H */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 21b5d9e..0696bcf 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -26,7 +26,7 @@ #include "pc.h" #include "sysemu.h" #include "boards.h" -#include "xen.h" +#include "xen_backend.h" uint32_t xen_domid; enum xen_mode xen_mode = XEN_EMULATE; @@ -50,6 +50,12 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, } env = cpu_init(cpu_model); env->halted = 1; + + /* Initialize backend core & drivers */ + if (-1 == xen_be_init()) { + fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); + exit(1); + } } QEMUMachine xenpv_machine = { -- 1.6.1.3 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-01 21:39 UTC
[Xen-devel] [PATCH 03/10] xen: add console backend driver.
This patch adds a xenconsole backend driver. It it based on current xen-unstable code. It has been changed to make use of the common backend driver code. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 1 + hw/xen_backend.h | 3 + hw/xen_console.c | 276 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_machine_pv.c | 1 + 4 files changed, 281 insertions(+), 0 deletions(-) create mode 100644 hw/xen_console.c diff --git a/Makefile.target b/Makefile.target index dd8c0cb..b22f7e1 100644 --- a/Makefile.target +++ b/Makefile.target @@ -565,6 +565,7 @@ endif # xen backend driver support XEN_OBJS := xen_machine_pv.o xen_backend.o +XEN_OBJS += xen_console.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.h b/hw/xen_backend.h index a1243f6..4744713 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -83,4 +83,7 @@ int xen_be_send_notify(struct XenDevice *xendev); void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) __attribute__ ((format(printf, 3, 4))); +/* actual backend drivers */ +extern struct XenDevOps xen_console_ops; /* xen_console.c */ + #endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_console.c b/hw/xen_console.c new file mode 100644 index 0000000..c172cf2 --- /dev/null +++ b/hw/xen_console.c @@ -0,0 +1,276 @@ +/* + * Copyright (C) International Business Machines Corp., 2005 + * Author(s): Anthony Liguori <aliguori@us.ibm.com> + * + * Copyright (C) Red Hat 2007 + * + * Xen Console + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/select.h> +#include <fcntl.h> +#include <unistd.h> +#include <termios.h> +#include <stdarg.h> +#include <sys/mman.h> +#include <xs.h> +#include <xen/io/console.h> +#include <xenctrl.h> + +#include "hw.h" +#include "sysemu.h" +#include "qemu-char.h" +#include "xen_backend.h" + +#define dolog(val, fmt, ...) fprintf(stderr, fmt "\n", ## __VA_ARGS__) + +struct buffer { + uint8_t *data; + size_t consumed; + size_t size; + size_t capacity; + size_t max_capacity; +}; + +struct XenConsole { + struct XenDevice xendev; /* must be first */ + struct buffer buffer; + char console[XEN_BUFSIZE]; + int ring_ref; + void *sring; + CharDriverState *chr; + int backlog; +}; + +static void buffer_append(struct XenConsole *con) +{ + struct buffer *buffer = &con->buffer; + XENCONS_RING_IDX cons, prod, size; + struct xencons_interface *intf = con->sring; + + cons = intf->out_cons; + prod = intf->out_prod; + xen_mb(); + + size = prod - cons; + if ((size == 0) || (size > sizeof(intf->out))) + return; + + if ((buffer->capacity - buffer->size) < size) { + buffer->capacity += (size + 1024); + buffer->data = qemu_realloc(buffer->data, buffer->capacity); + if (buffer->data == NULL) { + dolog(LOG_ERR, "Memory allocation failed"); + exit(ENOMEM); + } + } + + while (cons != prod) + buffer->data[buffer->size++] = intf->out[ + MASK_XENCONS_IDX(cons++, intf->out)]; + + xen_mb(); + intf->out_cons = cons; + xen_be_send_notify(&con->xendev); + + if (buffer->max_capacity && + buffer->size > buffer->max_capacity) { + /* Discard the middle of the data. */ + + size_t over = buffer->size - buffer->max_capacity; + uint8_t *maxpos = buffer->data + buffer->max_capacity; + + memmove(maxpos - over, maxpos, over); + buffer->data = qemu_realloc(buffer->data, buffer->max_capacity); + buffer->size = buffer->capacity = buffer->max_capacity; + + if (buffer->consumed > buffer->max_capacity - over) + buffer->consumed = buffer->max_capacity - over; + } +} + +static void buffer_advance(struct buffer *buffer, size_t len) +{ + buffer->consumed += len; + if (buffer->consumed == buffer->size) { + buffer->consumed = 0; + buffer->size = 0; + } +} + +static int ring_free_bytes(struct XenConsole *con) +{ + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX cons, prod, space; + + cons = intf->in_cons; + prod = intf->in_prod; + xen_mb(); + + space = prod - cons; + if (space > sizeof(intf->in)) + return 0; /* ring is screwed: ignore it */ + + return (sizeof(intf->in) - space); +} + +static int xencons_can_receive(void *opaque) +{ + struct XenConsole *con = opaque; + return ring_free_bytes(con); +} + +static void xencons_receive(void *opaque, const uint8_t *buf, int len) +{ + struct XenConsole *con = opaque; + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX prod; + int i, max; + + max = ring_free_bytes(con); + /* The can_receive() func limits this, but check again anyway */ + if (max < len) + len = max; + + prod = intf->in_prod; + for (i = 0; i < len; i++) { + intf->in[MASK_XENCONS_IDX(prod++, intf->in)] + buf[i]; + } + xen_wmb(); + intf->in_prod = prod; + xen_be_send_notify(&con->xendev); +} + +static void xencons_send(struct XenConsole *con) +{ + ssize_t len, size; + + size = con->buffer.size - con->buffer.consumed; + if (con->chr) + len = qemu_chr_write(con->chr, con->buffer.data + con->buffer.consumed, + size); + else + len = size; + if (len < 1) { + if (!con->backlog) { + con->backlog = 1; + xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n"); + } + } else { + buffer_advance(&con->buffer, len); + if (con->backlog && len == size) { + con->backlog = 0; + xen_be_printf(&con->xendev, 1, "backlog is gone\n"); + } + } +} + +/* -------------------------------------------------------------------- */ + +static int con_init(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + char *type, *dom; + + /* setup */ + dom = xs_get_domain_path(xenstore, con->xendev.dom); + snprintf(con->console, sizeof(con->console), "%s/console", dom); + free(dom); + + type = xenstore_read_str(con->console, "type"); + if (!type || 0 != strcmp(type, "ioemu")) { + xen_be_printf(xendev, 1, "not for me (type=%s)\n", type); + return -1; + } + + if (!serial_hds[con->xendev.dev]) + xen_be_printf(xendev, 1, "WARNING: serial line %d not configured\n", + con->xendev.dev); + else + con->chr = serial_hds[con->xendev.dev]; + + return 0; +} + +static int con_connect(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + int limit; + + if (-1 == xenstore_read_int(con->console, "ring-ref", &con->ring_ref)) + return -1; + if (-1 == xenstore_read_int(con->console, "port", &con->xendev.remote_port)) + return -1; + if (0 == xenstore_read_int(con->console, "limit", &limit)) + con->buffer.max_capacity = limit; + + con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom, + XC_PAGE_SIZE, + PROT_READ|PROT_WRITE, + con->ring_ref); + if (!con->sring) + return -1; + + xen_be_bind_evtchn(&con->xendev); + if (con->chr) + qemu_chr_add_handlers(con->chr, xencons_can_receive, xencons_receive, + NULL, con); + + xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", + con->ring_ref, + con->xendev.remote_port, + con->xendev.local_port, + con->buffer.max_capacity); + return 0; +} + +static void con_disconnect(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + + if (con->chr) + qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); + xen_be_unbind_evtchn(&con->xendev); + + if (con->sring) { + munmap(con->sring, XC_PAGE_SIZE); + con->sring = NULL; + } +} + +static void con_event(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + + buffer_append(con); + if (con->buffer.size - con->buffer.consumed) + xencons_send(con); +} + +/* -------------------------------------------------------------------- */ + +struct XenDevOps xen_console_ops = { + .size = sizeof(struct XenConsole), + .flags = DEVOPS_FLAG_IGNORE_STATE, + .init = con_init, + .connect = con_connect, + .event = con_event, + .disconnect = con_disconnect, +}; diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 0696bcf..cf2e0a3 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -56,6 +56,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); exit(1); } + xen_be_register("console", &xen_console_ops); } QEMUMachine xenpv_machine = { -- 1.6.1.3 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-01 21:39 UTC
[Xen-devel] [PATCH 04/10] xen: add framebuffer backend driver
This patch adds a frsamebuffer (and kbd+mouse) backend driver. It it based on current xen-unstable code. It has been changed to make use of the common backend driver code. It also has been changed to compile with xen headers older than release 3.3 Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen_backend.h | 4 + hw/xen_machine_pv.c | 5 + hw/xenfb.c | 1018 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1028 insertions(+), 1 deletions(-) create mode 100644 hw/xenfb.c diff --git a/Makefile.target b/Makefile.target index b22f7e1..e824675 100644 --- a/Makefile.target +++ b/Makefile.target @@ -565,7 +565,7 @@ endif # xen backend driver support XEN_OBJS := xen_machine_pv.o xen_backend.o -XEN_OBJS += xen_console.o +XEN_OBJS += xen_console.o xenfb.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.h b/hw/xen_backend.h index 4744713..e9a4e2d 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -85,5 +85,9 @@ void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ... /* actual backend drivers */ extern struct XenDevOps xen_console_ops; /* xen_console.c */ +extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ +extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ + +void xen_init_display(int domid); #endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index cf2e0a3..fee794a 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -57,6 +57,11 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, exit(1); } xen_be_register("console", &xen_console_ops); + xen_be_register("vkbd", &xen_kbdmouse_ops); + xen_be_register("vfb", &xen_framebuffer_ops); + + /* setup framebuffer */ + xen_init_display(xen_domid); } QEMUMachine xenpv_machine = { diff --git a/hw/xenfb.c b/hw/xenfb.c new file mode 100644 index 0000000..c9947d7 --- /dev/null +++ b/hw/xenfb.c @@ -0,0 +1,1018 @@ +/* + * xen paravirt framebuffer backend + * + * Copyright IBM, Corp. 2005-2006 + * Copyright Red Hat, Inc. 2006-2008 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com>, + * Markus Armbruster <armbru@redhat.com>, + * Daniel P. Berrange <berrange@redhat.com>, + * Pat Campbell <plc@novell.com>, + * Gerd Hoffmann <kraxel@redhat.com> + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdbool.h> +#include <sys/mman.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/event_channel.h> +#include <xen/io/xenbus.h> +#include <xen/io/fbif.h> +#include <xen/io/kbdif.h> +#include <xen/io/protocols.h> + +#include "hw.h" +#include "sysemu.h" +#include "console.h" +#include "qemu-char.h" +#include "xen_backend.h" + +#ifndef BTN_LEFT +#define BTN_LEFT 0x110 /* from <linux/input.h> */ +#endif + +/* -------------------------------------------------------------------- */ + +struct common { + struct XenDevice xendev; /* must be first */ + void *page; + DisplayState *ds; +}; + +struct XenInput { + struct common c; + int abs_pointer_wanted; /* Whether guest supports absolute pointer */ + int button_state; /* Last seen pointer button state */ + int extended; + QEMUPutMouseEntry *qmouse; +}; + +#define UP_QUEUE 8 + +struct XenFB { + struct common c; + size_t fb_len; + int row_stride; + int depth; + int width; + int height; + int offset; + void *pixels; + int fbpages; + int feature_update; + int refresh_period; + int bug_trigger; + int have_console; + int do_resize; + + struct { + int x,y,w,h; + } up_rects[UP_QUEUE]; + int up_count; + int up_fullscreen; +}; + +/* -------------------------------------------------------------------- */ + +static int common_bind(struct common *c) +{ + int mfn; + + if (-1 == xenstore_read_fe_int(&c->xendev, "page-ref", &mfn)) + return -1; + if (-1 == xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port)) + return -1; + + c->page = xc_map_foreign_range(xen_xc, c->xendev.dom, + XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, mfn); + if (NULL == c->page) + return -1; + + xen_be_bind_evtchn(&c->xendev); + xen_be_printf(&c->xendev, 1, "ring mfn %d, remote-port %d, local-port %d\n", + mfn, c->xendev.remote_port, c->xendev.local_port); + + return 0; +} + +static void common_unbind(struct common *c) +{ + xen_be_unbind_evtchn(&c->xendev); + if (c->page) { + munmap(c->page, XC_PAGE_SIZE); + c->page = NULL; + } +} + +/* -------------------------------------------------------------------- */ + +#if 0 +/* + * These two tables are not needed any more, but left in here + * intentionally as documentation, to show how scancode2linux[] + * was generated. + * + * Tables to map from scancode to Linux input layer keycode. + * Scancodes are hardware-specific. These maps assumes a + * standard AT or PS/2 keyboard which is what QEMU feeds us. + */ +const unsigned char atkbd_set2_keycode[512] = { + + 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, + 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, + 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, + 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, + 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, + 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85, + 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0, + 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, + 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, + 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142, + 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, + 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, + +}; + +const unsigned char atkbd_unxlate_table[128] = { + + 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, + 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, + 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, + 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, + 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 + +}; +#endif + +/* + * for (i = 0; i < 128; i++) { + * scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; + * scancode2linux[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; + * } + */ +static const unsigned char scancode2linux[512] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 99, 0, 86, 87, 88,117, 0, 0, 95,183,184,185, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 93, 0, 0, 89, 0, 0, 85, 91, 90, 92, 0, 94, 0,124,121, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 165, 0, 0, 0, 0, 0, 0, 0, 0,163, 0, 0, 96, 97, 0, 0, + 113,140,164, 0,166, 0, 0, 0, 0, 0,255, 0, 0, 0,114, 0, + 115, 0,150, 0, 0, 98,255, 99,100, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,119,119,102,103,104, 0,105,112,106,118,107, + 108,109,110,111, 0, 0, 0, 0, 0, 0, 0,125,126,127,116,142, + 0, 0, 0,143, 0,217,156,173,128,159,158,157,155,226, 0,112, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Send an event to the keyboard frontend driver */ +static int xenfb_kbd_event(struct XenInput *xenfb, + union xenkbd_in_event *event) +{ + struct xenkbd_page *page = xenfb->c.page; + uint32_t prod; + + if (xenfb->c.xendev.be_state != XenbusStateConnected) + return 0; + if (!page) + return 0; + + prod = page->in_prod; + if (prod - page->in_cons == XENKBD_IN_RING_LEN) { + errno = EAGAIN; + return -1; + } + + xen_mb(); /* ensure ring space available */ + XENKBD_IN_RING_REF(page, prod) = *event; + xen_wmb(); /* ensure ring contents visible */ + page->in_prod = prod + 1; + return xen_be_send_notify(&xenfb->c.xendev); +} + +/* Send a keyboard (or mouse button) event */ +static int xenfb_send_key(struct XenInput *xenfb, bool down, int keycode) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_KEY; + event.key.pressed = down ? 1 : 0; + event.key.keycode = keycode; + + return xenfb_kbd_event(xenfb, &event); +} + +/* Send a relative mouse movement event */ +static int xenfb_send_motion(struct XenInput *xenfb, + int rel_x, int rel_y, int rel_z) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_MOTION; + event.motion.rel_x = rel_x; + event.motion.rel_y = rel_y; +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207 + event.motion.rel_z = rel_z; +#endif + + return xenfb_kbd_event(xenfb, &event); +} + +/* Send an absolute mouse movement event */ +static int xenfb_send_position(struct XenInput *xenfb, + int abs_x, int abs_y, int z) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_POS; + event.pos.abs_x = abs_x; + event.pos.abs_y = abs_y; +#if __XEN_LATEST_INTERFACE_VERSION__ == 0x00030207 + event.pos.abs_z = z; +#endif +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030208 + event.pos.rel_z = z; +#endif + + return xenfb_kbd_event(xenfb, &event); +} + +/* + * Send a key event from the client to the guest OS + * QEMU gives us a raw scancode from an AT / PS/2 style keyboard. + * We have to turn this into a Linux Input layer keycode. + * + * Extra complexity from the fact that with extended scancodes + * (like those produced by arrow keys) this method gets called + * twice, but we only want to send a single event. So we have to + * track the ''0xe0'' scancode state & collapse the extended keys + * as needed. + * + * Wish we could just send scancodes straight to the guest which + * already has code for dealing with this... + */ +static void xenfb_key_event(void *opaque, int scancode) +{ + struct XenInput *xenfb = opaque; + int down = 1; + + if (scancode == 0xe0) { + xenfb->extended = 1; + return; + } else if (scancode & 0x80) { + scancode &= 0x7f; + down = 0; + } + if (xenfb->extended) { + scancode |= 0x80; + xenfb->extended = 0; + } + xenfb_send_key(xenfb, down, scancode2linux[scancode]); +} + +/* + * Send a mouse event from the client to the guest OS + * + * The QEMU mouse can be in either relative, or absolute mode. + * Movement is sent separately from button state, which has to + * be encoded as virtual key events. We also don''t actually get + * given any button up/down events, so have to track changes in + * the button state. + */ +static void xenfb_mouse_event(void *opaque, + int dx, int dy, int dz, int button_state) +{ + struct XenInput *xenfb = opaque; + int dw = ds_get_width(xenfb->c.ds); + int dh = ds_get_height(xenfb->c.ds); + int i; + + if (xenfb->abs_pointer_wanted) + xenfb_send_position(xenfb, + dx * (dw - 1) / 0x7fff, + dy * (dh - 1) / 0x7fff, + dz); + else + xenfb_send_motion(xenfb, dx, dy, dz); + + for (i = 0 ; i < 8 ; i++) { + int lastDown = xenfb->button_state & (1 << i); + int down = button_state & (1 << i); + if (down == lastDown) + continue; + + if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0) + return; + } + xenfb->button_state = button_state; +} + +static int input_init(struct XenDevice *xendev) +{ + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); + + if (!in->c.ds) { + xen_be_printf(xendev, 1, "ds not set (yet)\n"); + return -1; + } + + xenstore_write_be_int(xendev, "feature-abs-pointer", 1); + return 0; +} + +static int input_connect(struct XenDevice *xendev) +{ + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); + int rc; + + if (-1 == xenstore_read_fe_int(xendev, "request-abs-pointer", &in->abs_pointer_wanted)) + in->abs_pointer_wanted = 0; + + rc = common_bind(&in->c); + if (0 != rc) + return rc; + + qemu_add_kbd_event_handler(xenfb_key_event, in); + in->qmouse = qemu_add_mouse_event_handler(xenfb_mouse_event, in, + in->abs_pointer_wanted, + "Xen PVFB Mouse"); + return 0; +} + +static void input_disconnect(struct XenDevice *xendev) +{ + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); + + if (in->qmouse) { + qemu_remove_mouse_event_handler(in->qmouse); + in->qmouse = NULL; + } + qemu_add_kbd_event_handler(NULL, NULL); + common_unbind(&in->c); +} + +static void input_event(struct XenDevice *xendev) +{ + struct XenInput *xenfb = container_of(xendev, struct XenInput, c.xendev); + struct xenkbd_page *page = xenfb->c.page; + + /* We don''t understand any keyboard events, so just ignore them. */ + if (page->out_prod == page->out_cons) + return; + page->out_cons = page->out_prod; + xen_be_send_notify(&xenfb->c.xendev); +} + +/* -------------------------------------------------------------------- */ + +static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src) +{ + uint32_t *src32 = src; + uint64_t *src64 = src; + int i; + + for (i = 0; i < count; i++) + dst[i] = (mode == 32) ? src32[i] : src64[i]; +} + +static int xenfb_map_fb(struct XenFB *xenfb) +{ + struct xenfb_page *page = xenfb->c.page; + char *protocol = xenfb->c.xendev.protocol; + int n_fbdirs; + unsigned long *pgmfns = NULL; + unsigned long *fbmfns = NULL; + void *map, *pd; + int mode, ret = -1; + + /* default to native */ + pd = page->pd; + mode = sizeof(unsigned long) * 8; + + if (!protocol) { + /* + * Undefined protocol, some guesswork needed. + * + * Old frontends which don''t set the protocol use + * one page directory only, thus pd[1] must be zero. + * pd[1] of the 32bit struct layout and the lower + * 32 bits of pd[0] of the 64bit struct layout have + * the same location, so we can check that ... + */ + uint32_t *ptr32 = NULL; + uint32_t *ptr64 = NULL; +#if defined(__i386__) + ptr32 = (void*)page->pd; + ptr64 = ((void*)page->pd) + 4; +#elif defined(__x86_64__) + ptr32 = ((void*)page->pd) - 4; + ptr64 = (void*)page->pd; +#endif + if (ptr32) { + if (0 == ptr32[1]) { + mode = 32; + pd = ptr32; + } else { + mode = 64; + pd = ptr64; + } + } +#if defined(__x86_64__) + } else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_32)) { + /* 64bit dom0, 32bit domU */ + mode = 32; + pd = ((void*)page->pd) - 4; +#elif defined(__i386__) + } else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_64)) { + /* 32bit dom0, 64bit domU */ + mode = 64; + pd = ((void*)page->pd) + 4; +#endif + } + + if (xenfb->pixels) { + munmap(xenfb->pixels, xenfb->fbpages * XC_PAGE_SIZE); + xenfb->pixels = NULL; + } + + xenfb->fbpages = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + n_fbdirs = xenfb->fbpages * mode / 8; + n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + + pgmfns = qemu_mallocz(sizeof(unsigned long) * n_fbdirs); + fbmfns = qemu_mallocz(sizeof(unsigned long) * xenfb->fbpages); + if (!pgmfns || !fbmfns) + goto out; + + xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd); + map = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, + PROT_READ, pgmfns, n_fbdirs); + if (map == NULL) + goto out; + xenfb_copy_mfns(mode, xenfb->fbpages, fbmfns, map); + munmap(map, n_fbdirs * XC_PAGE_SIZE); + + xenfb->pixels = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, + PROT_READ | PROT_WRITE, fbmfns, xenfb->fbpages); + if (xenfb->pixels == NULL) + goto out; + + ret = 0; /* all is fine */ + +out: + qemu_free(pgmfns); + qemu_free(fbmfns); + return ret; +} + +static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim, + int width, int height, int depth, + size_t fb_len, int offset, int row_stride) +{ + size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd); + size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz; + size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz; + size_t fb_len_max = fb_pages * XC_PAGE_SIZE; + int max_width, max_height; + + if (fb_len_lim > fb_len_max) { + xen_be_printf(&xenfb->c.xendev, 0, "fb size limit %zu exceeds %zu, corrected\n", + fb_len_lim, fb_len_max); + fb_len_lim = fb_len_max; + } + if (fb_len_lim && fb_len > fb_len_lim) { + xen_be_printf(&xenfb->c.xendev, 0, "frontend fb size %zu limited to %zu\n", + fb_len, fb_len_lim); + fb_len = fb_len_lim; + } + if (depth != 8 && depth != 16 && depth != 24 && depth != 32) { + xen_be_printf(&xenfb->c.xendev, 0, "can''t handle frontend fb depth %d\n", + depth); + return -1; + } + if (row_stride <= 0 || row_stride > fb_len) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n", row_stride); + return -1; + } + max_width = row_stride / (depth / 8); + if (width < 0 || width > max_width) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend width %d limited to %d\n", + width, max_width); + width = max_width; + } + if (offset < 0 || offset >= fb_len) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend offset %d (max %zu)\n", + offset, fb_len - 1); + return -1; + } + max_height = (fb_len - offset) / row_stride; + if (height < 0 || height > max_height) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend height %d limited to %d\n", + height, max_height); + height = max_height; + } + xenfb->fb_len = fb_len; + xenfb->row_stride = row_stride; + xenfb->depth = depth; + xenfb->width = width; + xenfb->height = height; + xenfb->offset = offset; + xenfb->up_fullscreen = 1; + xenfb->do_resize = 1; + xen_be_printf(&xenfb->c.xendev, 1, "framebuffer %dx%dx%d offset %d stride %d\n", + width, height, depth, offset, row_stride); + return 0; +} + +/* A convenient function for munging pixels between different depths */ +#define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \ + for (line = y ; line < (y+h) ; line++) { \ + SRC_T *src = (SRC_T *)(xenfb->pixels \ + + xenfb->offset \ + + (line * xenfb->row_stride) \ + + (x * xenfb->depth / 8)); \ + DST_T *dst = (DST_T *)(data \ + + (line * linesize) \ + + (x * bpp / 8)); \ + int col; \ + const int RSS = 32 - (RSB + GSB + BSB); \ + const int GSS = 32 - (GSB + BSB); \ + const int BSS = 32 - (BSB); \ + const uint32_t RSM = (~0U) << (32 - RSB); \ + const uint32_t GSM = (~0U) << (32 - GSB); \ + const uint32_t BSM = (~0U) << (32 - BSB); \ + const int RDS = 32 - (RDB + GDB + BDB); \ + const int GDS = 32 - (GDB + BDB); \ + const int BDS = 32 - (BDB); \ + const uint32_t RDM = (~0U) << (32 - RDB); \ + const uint32_t GDM = (~0U) << (32 - GDB); \ + const uint32_t BDM = (~0U) << (32 - BDB); \ + for (col = x ; col < (x+w) ; col++) { \ + uint32_t spix = *src; \ + *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \ + (((spix << GSS) & GSM & GDM) >> GDS) | \ + (((spix << BSS) & BSM & BDM) >> BDS); \ + src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \ + dst = (DST_T *) ((unsigned long) dst + bpp / 8); \ + } \ + } + + +/* + * This copies data from the guest framebuffer region, into QEMU''s + * displaysurface. qemu uses 16 or 32 bpp. In case the pv framebuffer + * uses something else we must convert and copy, otherwise we can + * supply the buffer directly and no thing here. + */ +static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h) +{ + int line, oops = 0; + int bpp = ds_get_bits_per_pixel(xenfb->c.ds); + int linesize = ds_get_linesize(xenfb->c.ds); + uint8_t *data = ds_get_data(xenfb->c.ds); + + if (!is_buffer_shared(xenfb->c.ds->surface)) { + switch (xenfb->depth) { + case 8: + if (bpp == 16) { + BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5); + } else if (bpp == 32) { + BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8); + } else { + oops = 1; + } + break; + case 24: + if (bpp == 16) { + BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5); + } else if (bpp == 32) { + BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8); + } else { + oops = 1; + } + break; + default: + oops = 1; + } + } + if (oops) /* should not happen */ + xen_be_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n", + __FUNCTION__, xenfb->depth, bpp); + + dpy_update(xenfb->c.ds, x, y, w, h); +} + +#ifdef XENFB_TYPE_REFRESH_PERIOD +static int xenfb_queue_full(struct XenFB *xenfb) +{ + struct xenfb_page *page = xenfb->c.page; + uint32_t cons, prod; + + if (!page) + return 1; + + prod = page->in_prod; + cons = page->in_cons; + return prod - cons == XENFB_IN_RING_LEN; +} + +static void xenfb_send_event(struct XenFB *xenfb, union xenfb_in_event *event) +{ + uint32_t prod; + struct xenfb_page *page = xenfb->c.page; + + prod = page->in_prod; + /* caller ensures !xenfb_queue_full() */ + xen_mb(); /* ensure ring space available */ + XENFB_IN_RING_REF(page, prod) = *event; + xen_wmb(); /* ensure ring contents visible */ + page->in_prod = prod + 1; + + xen_be_send_notify(&xenfb->c.xendev); +} + +static void xenfb_send_refresh_period(struct XenFB *xenfb, int period) +{ + union xenfb_in_event event; + + memset(&event, 0, sizeof(event)); + event.type = XENFB_TYPE_REFRESH_PERIOD; + event.refresh_period.period = period; + xenfb_send_event(xenfb, &event); +} +#endif + +/* + * Periodic update of display. + * Also transmit the refresh interval to the frontend. + * + * Never ever do any qemu display operations + * (resize, screen update) outside this function. + * Our screen might be inactive. When asked for + * an update we know it is active. + */ +static void xenfb_update(void *opaque) +{ + struct XenFB *xenfb = opaque; + struct DisplayChangeListener *l; + int dw = ds_get_width(xenfb->c.ds); + int dh = ds_get_height(xenfb->c.ds); + int i; + + if (xenfb->c.xendev.be_state != XenbusStateConnected) + return; + + if (xenfb->feature_update) { +#ifdef XENFB_TYPE_REFRESH_PERIOD + int period = 99999999; + int idle = 1; + + if (xenfb_queue_full(xenfb)) + return; + + for (l = xenfb->c.ds->listeners; l != NULL; l = l->next) { + if (l->idle) + continue; + idle = 0; + if (!l->gui_timer_interval) { + if (period > GUI_REFRESH_INTERVAL) + period = GUI_REFRESH_INTERVAL; + } else { + if (period > l->gui_timer_interval) + period = l->gui_timer_interval; + } + } + if (idle) + period = XENFB_NO_REFRESH; + + if (xenfb->refresh_period != period) { + xenfb_send_refresh_period(xenfb, period); + xenfb->refresh_period = period; + xen_be_printf(&xenfb->c.xendev, 1, "refresh period: %d\n", period); + } +#else + ; /* nothing */ +#endif + } else { + /* we don''t get update notifications, thus use the + * sledge hammer approach ... */ + xenfb->up_fullscreen = 1; + } + + /* resize if needed */ + if (xenfb->do_resize) { + xenfb->do_resize = 0; + switch (xenfb->depth) { + case 16: + case 32: + /* console.c supported depth -> buffer can be used directly */ + qemu_free_displaysurface(xenfb->c.ds); + xenfb->c.ds->surface = qemu_create_displaysurface_from + (xenfb->width, xenfb->height, xenfb->depth, + xenfb->row_stride, xenfb->pixels + xenfb->offset); + break; + default: + /* we must convert stuff */ + qemu_resize_displaysurface(xenfb->c.ds, + xenfb->width, xenfb->height); + break; + } + xen_be_printf(&xenfb->c.xendev, 1, "update: resizing: %dx%d @ %d bpp%s\n", + xenfb->width, xenfb->height, xenfb->depth, + is_buffer_shared(xenfb->c.ds->surface) ? " (shared)" : ""); + dpy_resize(xenfb->c.ds); + xenfb->up_fullscreen = 1; + } + + /* run queued updates */ + if (xenfb->up_fullscreen) { + xen_be_printf(&xenfb->c.xendev, 3, "update: fullscreen\n"); + xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height); + } else if (xenfb->up_count) { + xen_be_printf(&xenfb->c.xendev, 3, "update: %d rects\n", xenfb->up_count); + for (i = 0; i < xenfb->up_count; i++) + xenfb_guest_copy(xenfb, + xenfb->up_rects[i].x, + xenfb->up_rects[i].y, + xenfb->up_rects[i].w, + xenfb->up_rects[i].h); + } else { + xen_be_printf(&xenfb->c.xendev, 3, "update: nothing\n"); + } + xenfb->up_count = 0; + xenfb->up_fullscreen = 0; +} + +/* QEMU display state changed, so refresh the framebuffer copy */ +static void xenfb_invalidate(void *opaque) +{ + struct XenFB *xenfb = opaque; + xenfb->up_fullscreen = 1; +} + +static void xenfb_handle_events(struct XenFB *xenfb) +{ + uint32_t prod, cons; + struct xenfb_page *page = xenfb->c.page; + + prod = page->out_prod; + if (prod == page->out_cons) + return; + xen_rmb(); /* ensure we see ring contents up to prod */ + for (cons = page->out_cons; cons != prod; cons++) { + union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons); + int x, y, w, h; + + switch (event->type) { + case XENFB_TYPE_UPDATE: + if (xenfb->up_count == UP_QUEUE) + xenfb->up_fullscreen = 1; + if (xenfb->up_fullscreen) + break; + x = MAX(event->update.x, 0); + y = MAX(event->update.y, 0); + w = MIN(event->update.width, xenfb->width - x); + h = MIN(event->update.height, xenfb->height - y); + if (w < 0 || h < 0) { + fprintf(stderr, "xen be: %s: bogus update ignored\n", + xenfb->c.xendev.name); + break; + } + if (x != event->update.x || y != event->update.y + || w != event->update.width + || h != event->update.height) { + fprintf(stderr, "xen be: %s: bogus update clipped\n", + xenfb->c.xendev.name); + } + if (w == xenfb->width && h > xenfb->height / 2) { + /* scroll detector: updated more than 50% of the lines, + * don''t bother keeping track of the rectangles then */ + xenfb->up_fullscreen = 1; + } else { + xenfb->up_rects[xenfb->up_count].x = x; + xenfb->up_rects[xenfb->up_count].y = y; + xenfb->up_rects[xenfb->up_count].w = w; + xenfb->up_rects[xenfb->up_count].h = h; + xenfb->up_count++; + } + break; +#ifdef XENFB_TYPE_RESIZE + case XENFB_TYPE_RESIZE: + if (xenfb_configure_fb(xenfb, xenfb->fb_len, + event->resize.width, + event->resize.height, + event->resize.depth, + xenfb->fb_len, + event->resize.offset, + event->resize.stride) < 0) + break; + xenfb_invalidate(xenfb); + break; +#endif + } + } + xen_mb(); /* ensure we''re done with ring contents */ + page->out_cons = cons; +} + +static int fb_init(struct XenDevice *xendev) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + + fb->refresh_period = -1; + +#ifdef XENFB_TYPE_RESIZE + xenstore_write_be_int(xendev, "feature-resize", 1); +#endif + return 0; +} + +static int fb_connect(struct XenDevice *xendev) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + struct xenfb_page *fb_page; + int videoram; + int rc; + + if (-1 == xenstore_read_fe_int(xendev, "videoram", &videoram)) + videoram = 0; + + rc = common_bind(&fb->c); + if (0 != rc) + return rc; + + fb_page = fb->c.page; + rc = xenfb_configure_fb(fb, videoram * 1024 * 1024U, + fb_page->width, fb_page->height, fb_page->depth, + fb_page->mem_length, 0, fb_page->line_length); + if (0 != rc) + return rc; + + rc = xenfb_map_fb(fb); + if (0 != rc) + return rc; + +#if 0 /* handled in xen_init_display() for now */ + if (!fb->have_console) { + fb->c.ds = graphic_console_init(xenfb_update, + xenfb_invalidate, + NULL, + NULL, + fb); + fb->have_console = 1; + } +#endif + + if (-1 == xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update)) + fb->feature_update = 0; + if (fb->feature_update) + xenstore_write_be_int(xendev, "request-update", 1); + + xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n", + fb->feature_update, videoram); + return 0; +} + +static void fb_disconnect(struct XenDevice *xendev) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + + /* + * FIXME: qemu can''t un-init gfx display (yet?). + * Replacing the framebuffer with anonymous shared memory + * instead. This releases the guest pages and keeps qemu happy. + */ + fb->pixels = mmap(fb->pixels, fb->fbpages * XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, + -1, 0); + common_unbind(&fb->c); + fb->feature_update = 0; + fb->bug_trigger = 0; +} + +static void fb_frontend_changed(struct XenDevice *xendev, const char *node) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + + /* + * Set state to Connected *again* once the frontend switched + * to connected. We must trigger the watch a second time to + * workaround a frontend bug. + */ + if (0 == fb->bug_trigger && 0 == strcmp(node, "state") && + xendev->fe_state == XenbusStateConnected && + xendev->be_state == XenbusStateConnected) { + xen_be_printf(xendev, 2, "re-trigger connected (frontend bug)\n"); + xen_be_set_state(xendev, XenbusStateConnected); + fb->bug_trigger = 1; /* only once */ + } +} + +static void fb_event(struct XenDevice *xendev) +{ + struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev); + + xenfb_handle_events(xenfb); + xen_be_send_notify(&xenfb->c.xendev); +} + +/* -------------------------------------------------------------------- */ + +struct XenDevOps xen_kbdmouse_ops = { + .size = sizeof(struct XenInput), + .init = input_init, + .connect = input_connect, + .disconnect = input_disconnect, + .event = input_event, +}; + +struct XenDevOps xen_framebuffer_ops = { + .size = sizeof(struct XenFB), + .init = fb_init, + .connect = fb_connect, + .disconnect = fb_disconnect, + .event = fb_event, + .frontend_changed = fb_frontend_changed, +}; + +/* + * FIXME/TODO: Kill this. + * Temporary needed while DisplayState reorganization is in flight. + */ +void xen_init_display(int domid) +{ + struct XenDevice *xfb, *xin; + struct XenFB *fb; + struct XenInput *in; + int i = 0; + +wait_more: + i++; + main_loop_wait(10); /* miliseconds */ + xfb = xen_be_find_xendev("vfb", domid, 0); + xin = xen_be_find_xendev("vkbd", domid, 0); + if (!xfb || !xin) { + if (i < 256) + goto wait_more; + fprintf(stderr, "%s: displaystate setup failed\n", __FUNCTION__); + return; + } + + /* vfb */ + fb = container_of(xfb, struct XenFB, c.xendev); + fb->c.ds = graphic_console_init(xenfb_update, + xenfb_invalidate, + NULL, + NULL, + fb); + fb->have_console = 1; + + /* vkbd */ + in = container_of(xin, struct XenInput, c.xendev); + in->c.ds = fb->c.ds; + + /* retry ->init() */ + xen_be_check_state(xin); + xen_be_check_state(xfb); +} -- 1.6.1.3 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-01 21:39 UTC
[Xen-devel] [PATCH 05/10] xen: add block device backend driver.
This patch adds a block device backend driver to qemu. It is a pure userspace implemention using the gntdev interface. It uses "qdisk" as backend name in xenstore so it doesn''t interfere with the other existing backends (blkback aka "vbd" and tapdisk aka "tap"). Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen_backend.h | 2 + hw/xen_blkif.h | 103 +++++++ hw/xen_disk.c | 799 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_machine_pv.c | 1 + sysemu.h | 2 +- vl.c | 6 +- 7 files changed, 912 insertions(+), 3 deletions(-) create mode 100644 hw/xen_blkif.h create mode 100644 hw/xen_disk.c diff --git a/Makefile.target b/Makefile.target index e824675..b66ff27 100644 --- a/Makefile.target +++ b/Makefile.target @@ -565,7 +565,7 @@ endif # xen backend driver support XEN_OBJS := xen_machine_pv.o xen_backend.o -XEN_OBJS += xen_console.o xenfb.o +XEN_OBJS += xen_console.o xenfb.o xen_disk.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.h b/hw/xen_backend.h index e9a4e2d..dd426dd 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -2,6 +2,7 @@ #define QEMU_HW_XEN_BACKEND_H 1 #include "xen_common.h" +#include "sysemu.h" /* ------------------------------------------------------------- */ @@ -87,6 +88,7 @@ void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ... extern struct XenDevOps xen_console_ops; /* xen_console.c */ extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ +extern struct XenDevOps xen_blkdev_ops; /* xen_disk.c */ void xen_init_display(int domid); diff --git a/hw/xen_blkif.h b/hw/xen_blkif.h new file mode 100644 index 0000000..254a5fd --- /dev/null +++ b/hw/xen_blkif.h @@ -0,0 +1,103 @@ +#ifndef __XEN_BLKIF_H__ +#define __XEN_BLKIF_H__ + +#include <xen/io/ring.h> +#include <xen/io/blkif.h> +#include <xen/io/protocols.h> + +/* Not a real protocol. Used to generate ring structs which contain + * the elements common to all protocols only. This way we get a + * compiler-checkable way to use common struct elements, so we can + * avoid using switch(protocol) in a number of places. */ +struct blkif_common_request { + char dummy; +}; +struct blkif_common_response { + char dummy; +}; + +/* i386 protocol version */ +#pragma pack(push, 4) +struct blkif_x86_32_request { + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t id; /* private guest value, echoed in resp */ + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; +struct blkif_x86_32_response { + uint64_t id; /* copied from request */ + uint8_t operation; /* copied from request */ + int16_t status; /* BLKIF_RSP_??? */ +}; +typedef struct blkif_x86_32_request blkif_x86_32_request_t; +typedef struct blkif_x86_32_response blkif_x86_32_response_t; +#pragma pack(pop) + +/* x86_64 protocol version */ +struct blkif_x86_64_request { + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t __attribute__((__aligned__(8))) id; + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; +struct blkif_x86_64_response { + uint64_t __attribute__((__aligned__(8))) id; + uint8_t operation; /* copied from request */ + int16_t status; /* BLKIF_RSP_??? */ +}; +typedef struct blkif_x86_64_request blkif_x86_64_request_t; +typedef struct blkif_x86_64_response blkif_x86_64_response_t; + +DEFINE_RING_TYPES(blkif_common, struct blkif_common_request, struct blkif_common_response); +DEFINE_RING_TYPES(blkif_x86_32, struct blkif_x86_32_request, struct blkif_x86_32_response); +DEFINE_RING_TYPES(blkif_x86_64, struct blkif_x86_64_request, struct blkif_x86_64_response); + +union blkif_back_rings { + blkif_back_ring_t native; + blkif_common_back_ring_t common; + blkif_x86_32_back_ring_t x86_32; + blkif_x86_64_back_ring_t x86_64; +}; +typedef union blkif_back_rings blkif_back_rings_t; + +enum blkif_protocol { + BLKIF_PROTOCOL_NATIVE = 1, + BLKIF_PROTOCOL_X86_32 = 2, + BLKIF_PROTOCOL_X86_64 = 3, +}; + +static void inline blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_request_t *src) +{ + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; + + dst->operation = src->operation; + dst->nr_segments = src->nr_segments; + dst->handle = src->handle; + dst->id = src->id; + dst->sector_number = src->sector_number; + if (n > src->nr_segments) + n = src->nr_segments; + for (i = 0; i < n; i++) + dst->seg[i] = src->seg[i]; +} + +static void inline blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_request_t *src) +{ + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; + + dst->operation = src->operation; + dst->nr_segments = src->nr_segments; + dst->handle = src->handle; + dst->id = src->id; + dst->sector_number = src->sector_number; + if (n > src->nr_segments) + n = src->nr_segments; + for (i = 0; i < n; i++) + dst->seg[i] = src->seg[i]; +} + +#endif /* __XEN_BLKIF_H__ */ diff --git a/hw/xen_disk.c b/hw/xen_disk.c new file mode 100644 index 0000000..33b2dfd --- /dev/null +++ b/hw/xen_disk.c @@ -0,0 +1,799 @@ +/* + * xen paravirt block device backend + * + * (c) Gerd Hoffmann <kraxel@redhat.com> + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * FIXME: the code is designed to handle multiple outstanding + * requests, which isn''t used right now. Plan is to + * switch over to the aio block functions once they got + * vector support. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <inttypes.h> +#include <time.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/uio.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> + +#include "hw.h" +#include "block_int.h" +#include "qemu-char.h" +#include "xen_blkif.h" +#include "xen_backend.h" + +/* ------------------------------------------------------------- */ + +static int syncwrite = 0; +static int batch_maps = 0; + +static int max_requests = 32; +static int use_aio = 1; + +/* ------------------------------------------------------------- */ + +#define BLOCK_SIZE 512 +#define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2) + +struct ioreq { + blkif_request_t req; + int16_t status; + + /* parsed request */ + off_t start, end; + struct iovec vec[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + int vecs; + int presync; + int postsync; + + /* grant mapping */ + uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + int prot; + void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + void *pages; + + /* aio status */ + int aio_inflight; + int aio_errors; + + struct XenBlkDev *blkdev; + LIST_ENTRY(ioreq) list; +}; + +struct XenBlkDev { + struct XenDevice xendev; /* must be first */ + char *params; + char *mode; + char *type; + char *dev; + char *devtype; + const char *fileproto; + const char *filename; + int ring_ref; + void *sring; + int64_t file_blk; + int64_t file_size; + int protocol; + blkif_back_rings_t rings; + int more_work; + int cnt_map; + + /* request lists */ + LIST_HEAD(inflight_head, ioreq) inflight; + LIST_HEAD(finished_head, ioreq) finished; + LIST_HEAD(freelist_head, ioreq) freelist; + int requests_total; + int requests_inflight; + int requests_finished; + + /* qemu block driver */ + int index; + BlockDriverState *bs; + QEMUBH *bh; +}; + +/* ------------------------------------------------------------- */ + +static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) +{ + struct ioreq *ioreq = NULL; + + if (LIST_EMPTY(&blkdev->freelist)) { + if (blkdev->requests_total >= max_requests) + goto out; + /* allocate new struct */ + ioreq = qemu_mallocz(sizeof(*ioreq)); + if (NULL == ioreq) + goto out; + ioreq->blkdev = blkdev; + blkdev->requests_total++; + } else { + /* get one from freelist */ + ioreq = LIST_FIRST(&blkdev->freelist); + LIST_REMOVE(ioreq, list); + } + LIST_INSERT_HEAD(&blkdev->inflight, ioreq, list); + blkdev->requests_inflight++; + +out: + return ioreq; +} + +static void ioreq_finish(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + + LIST_REMOVE(ioreq, list); + LIST_INSERT_HEAD(&blkdev->finished, ioreq, list); + blkdev->requests_inflight--; + blkdev->requests_finished++; +} + +static void ioreq_release(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + + LIST_REMOVE(ioreq, list); + memset(ioreq, 0, sizeof(*ioreq)); + ioreq->blkdev = blkdev; + LIST_INSERT_HEAD(&blkdev->freelist, ioreq, list); + blkdev->requests_finished--; +} + +/* + * translate request into iovec + start offset + end offset + * do sanity checks along the way + */ +static int ioreq_parse(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + uintptr_t mem; + size_t len; + int i; + + xen_be_printf(&blkdev->xendev, 3, + "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", + ioreq->req.operation, ioreq->req.nr_segments, + ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + ioreq->prot = PROT_WRITE; /* to memory */ + if (BLKIF_OP_READ != ioreq->req.operation && blkdev->mode[0] != ''w'') { + xen_be_printf(&blkdev->xendev, 0, "error: write req for ro device\n"); + goto err; + } + break; + case BLKIF_OP_WRITE_BARRIER: + if (!syncwrite) + ioreq->presync = ioreq->postsync = 1; + /* fall through */ + case BLKIF_OP_WRITE: + ioreq->prot = PROT_READ; /* from memory */ + if (syncwrite) + ioreq->postsync = 1; + break; + default: + xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", + ioreq->req.operation); + goto err; + }; + + ioreq->start = ioreq->end = ioreq->req.sector_number * blkdev->file_blk; + for (i = 0; i < ioreq->req.nr_segments; i++) { + if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { + xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); + goto err; + } + if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { + xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n"); + goto err; + } + if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { + xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n"); + goto err; + } + len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; + + ioreq->domids[i] = blkdev->xendev.dom; + ioreq->refs[i] = ioreq->req.seg[i].gref; + mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; + + ioreq->vec[i].iov_base = (void*)mem; + ioreq->vec[i].iov_len = len; + ioreq->end += len; + } + if (ioreq->end > blkdev->file_size) { + xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); + goto err; + } + ioreq->vecs = i; + return 0; + +err: + ioreq->status = BLKIF_RSP_ERROR; + return -1; +} + +static void ioreq_unmap(struct ioreq *ioreq) +{ + int gnt = ioreq->blkdev->xendev.gnttabdev; + int i; + + if (0 == ioreq->vecs) + return; + if (batch_maps) { + if (!ioreq->pages) + return; + if (0 != xc_gnttab_munmap(gnt, ioreq->pages, ioreq->vecs)) + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + ioreq->blkdev->cnt_map -= ioreq->vecs; + ioreq->pages = NULL; + } else { + for (i = 0; i < ioreq->vecs; i++) { + if (!ioreq->page[i]) + continue; + if (0 != xc_gnttab_munmap(gnt, ioreq->page[i], 1)) + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + ioreq->blkdev->cnt_map--; + ioreq->page[i] = NULL; + } + } +} + +static int ioreq_map(struct ioreq *ioreq) +{ + int gnt = ioreq->blkdev->xendev.gnttabdev; + int i; + + if (0 == ioreq->vecs) + return 0; + if (batch_maps) { + ioreq->pages = xc_gnttab_map_grant_refs + (gnt, ioreq->vecs, ioreq->domids, ioreq->refs, ioreq->prot); + if (NULL == ioreq->pages) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can''t map %d grant refs (%s, %d maps)\n", + ioreq->vecs, strerror(errno), ioreq->blkdev->cnt_map); + return -1; + } + for (i = 0; i < ioreq->vecs; i++) + ioreq->vec[i].iov_base = ioreq->pages + i * XC_PAGE_SIZE + + (uintptr_t)ioreq->vec[i].iov_base; + ioreq->blkdev->cnt_map += ioreq->vecs; + } else { + for (i = 0; i < ioreq->vecs; i++) { + ioreq->page[i] = xc_gnttab_map_grant_ref + (gnt, ioreq->domids[i], ioreq->refs[i], ioreq->prot); + if (NULL == ioreq->page[i]) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can''t map grant ref %d (%s, %d maps)\n", + ioreq->refs[i], strerror(errno), ioreq->blkdev->cnt_map); + ioreq_unmap(ioreq); + return -1; + } + ioreq->vec[i].iov_base = ioreq->page[i] + (uintptr_t)ioreq->vec[i].iov_base; + ioreq->blkdev->cnt_map++; + } + } + return 0; +} + +static int ioreq_runio_qemu_sync(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + int i, rc, len = 0; + off_t pos; + + if (-1 == ioreq_map(ioreq)) + goto err; + if (ioreq->presync) + bdrv_flush(blkdev->bs); + + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + pos = ioreq->start; + for (i = 0; i < ioreq->vecs; i++) { + rc = bdrv_read(blkdev->bs, pos / BLOCK_SIZE, + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "rd I/O error (%p, len %zd)\n", + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len); + goto err; + } + len += ioreq->vec[i].iov_len; + pos += ioreq->vec[i].iov_len; + } + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_WRITE_BARRIER: + pos = ioreq->start; + for (i = 0; i < ioreq->vecs; i++) { + rc = bdrv_write(blkdev->bs, pos / BLOCK_SIZE, + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "wr I/O error (%p, len %zd)\n", + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len); + goto err; + } + len += ioreq->vec[i].iov_len; + pos += ioreq->vec[i].iov_len; + } + break; + default: + /* unknown operation (shouldn''t happen -- parse catches this) */ + goto err; + } + + if (ioreq->postsync) + bdrv_flush(blkdev->bs); + ioreq->status = BLKIF_RSP_OKAY; + + ioreq_unmap(ioreq); + ioreq_finish(ioreq); + return 0; + +err: + ioreq->status = BLKIF_RSP_ERROR; + return -1; +} + +static void qemu_aio_complete(void *opaque, int ret) +{ + struct ioreq *ioreq = opaque; + + if (ret != 0) { + xen_be_printf(&ioreq->blkdev->xendev, 0, "%s I/O error\n", + ioreq->req.operation == BLKIF_OP_READ ? "read" : "write"); + ioreq->aio_errors++; + } + + ioreq->aio_inflight--; + if (ioreq->aio_inflight > 0) + return; + + ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; + ioreq_unmap(ioreq); + ioreq_finish(ioreq); + qemu_bh_schedule(ioreq->blkdev->bh); +} + +static int ioreq_runio_qemu_aio(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + int i, len = 0; + off_t pos; + + if (-1 == ioreq_map(ioreq)) + goto err; + + ioreq->aio_inflight++; + if (ioreq->presync) + bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */ + + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + pos = ioreq->start; + for (i = 0; i < ioreq->vecs; i++) { + ioreq->aio_inflight++; + bdrv_aio_read(blkdev->bs, pos / BLOCK_SIZE, + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len / BLOCK_SIZE, + qemu_aio_complete, ioreq); + len += ioreq->vec[i].iov_len; + pos += ioreq->vec[i].iov_len; + } + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_WRITE_BARRIER: + pos = ioreq->start; + for (i = 0; i < ioreq->vecs; i++) { + ioreq->aio_inflight++; + bdrv_aio_write(blkdev->bs, pos / BLOCK_SIZE, + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len / BLOCK_SIZE, + qemu_aio_complete, ioreq); + len += ioreq->vec[i].iov_len; + pos += ioreq->vec[i].iov_len; + } + break; + default: + /* unknown operation (shouldn''t happen -- parse catches this) */ + goto err; + } + + if (ioreq->postsync) + bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */ + qemu_aio_complete(ioreq, 0); + + return 0; + +err: + ioreq->status = BLKIF_RSP_ERROR; + return -1; +} + +static int blk_send_response_one(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + int send_notify = 0; + int have_requests = 0; + blkif_response_t resp; + void *dst; + + resp.id = ioreq->req.id; + resp.operation = ioreq->req.operation; + resp.status = ioreq->status; + + /* Place on the response ring for the relevant domain. */ + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + dst = RING_GET_RESPONSE(&blkdev->rings.native, blkdev->rings.native.rsp_prod_pvt); + break; + case BLKIF_PROTOCOL_X86_32: + dst = RING_GET_RESPONSE(&blkdev->rings.x86_32, blkdev->rings.x86_32.rsp_prod_pvt); + break; + case BLKIF_PROTOCOL_X86_64: + dst = RING_GET_RESPONSE(&blkdev->rings.x86_64, blkdev->rings.x86_64.rsp_prod_pvt); + break; + default: + dst = NULL; + } + memcpy(dst, &resp, sizeof(resp)); + blkdev->rings.common.rsp_prod_pvt++; + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify); + if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) { + /* + * Tail check for pending requests. Allows frontend to avoid + * notifications if requests are already in flight (lower + * overheads and promotes batching). + */ + RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests); + } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) { + have_requests = 1; + } + + if (have_requests) + blkdev->more_work++; + return send_notify; +} + +/* walk finished list, send outstanding responses, free requests */ +static void blk_send_response_all(struct XenBlkDev *blkdev) +{ + struct ioreq *ioreq; + int send_notify = 0; + + while (!LIST_EMPTY(&blkdev->finished)) { + ioreq = LIST_FIRST(&blkdev->finished); + send_notify += blk_send_response_one(ioreq); + ioreq_release(ioreq); + } + if (send_notify) + xen_be_send_notify(&blkdev->xendev); +} + +static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_IDX rc) +{ + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc), + sizeof(ioreq->req)); + break; + case BLKIF_PROTOCOL_X86_32: + blkif_get_x86_32_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_32, rc)); + break; + case BLKIF_PROTOCOL_X86_64: + blkif_get_x86_64_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_64, rc)); + break; + } + return 0; +} + +static void blk_handle_requests(struct XenBlkDev *blkdev) +{ + RING_IDX rc, rp; + struct ioreq *ioreq; + + blkdev->more_work = 0; + + rc = blkdev->rings.common.req_cons; + rp = blkdev->rings.common.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + if (use_aio) + blk_send_response_all(blkdev); + while ((rc != rp)) { + /* pull request from ring */ + if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) + break; + ioreq = ioreq_start(blkdev); + if (NULL == ioreq) { + blkdev->more_work++; + break; + } + blk_get_request(blkdev, ioreq, rc); + blkdev->rings.common.req_cons = ++rc; + + /* parse them */ + if (0 != ioreq_parse(ioreq)) { + if (blk_send_response_one(ioreq)) + xen_be_send_notify(&blkdev->xendev); + ioreq_release(ioreq); + continue; + } + + if (use_aio) { + /* run i/o in aio mode */ + ioreq_runio_qemu_aio(ioreq); + } else { + /* run i/o in sync mode */ + ioreq_runio_qemu_sync(ioreq); + } + } + if (!use_aio) + blk_send_response_all(blkdev); + + if (blkdev->more_work && blkdev->requests_inflight < max_requests) + qemu_bh_schedule(blkdev->bh); +} + +/* ------------------------------------------------------------- */ + +static void blk_bh(void *opaque) +{ + struct XenBlkDev *blkdev = opaque; + blk_handle_requests(blkdev); +} + +static void blk_alloc(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + LIST_INIT(&blkdev->inflight); + LIST_INIT(&blkdev->finished); + LIST_INIT(&blkdev->freelist); + blkdev->bh = qemu_bh_new(blk_bh, blkdev); + if (xen_mode != XEN_EMULATE) + batch_maps = 1; +} + +static int blk_init(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + int mode, qflags, have_barriers, info = 0; + char *h; + + /* read xenstore entries */ + if (NULL == blkdev->params) { + blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); + if (NULL != (h = strchr(blkdev->params, '':''))) { + blkdev->fileproto = blkdev->params; + blkdev->filename = h+1; + *h = 0; + } else { + blkdev->fileproto = "<unset>"; + blkdev->filename = blkdev->params; + } + } + if (NULL == blkdev->mode) + blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); + if (NULL == blkdev->type) + blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); + if (NULL == blkdev->dev) + blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); + if (NULL == blkdev->devtype) + blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type"); + + /* do we have all we need? */ + if (NULL == blkdev->params || + NULL == blkdev->mode || + NULL == blkdev->type || + NULL == blkdev->dev) + return -1; + + /* read-only ? */ + if (0 == strcmp(blkdev->mode, "w")) { + mode = O_RDWR; + qflags = BDRV_O_RDWR; + } else { + mode = O_RDONLY; + qflags = BDRV_O_RDONLY; + info |= VDISK_READONLY; + } + + /* cdrom ? */ + if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom")) + info |= VDISK_CDROM; + + /* init qemu block driver */ + blkdev->index = (blkdev->xendev.dev - 202 * 256) / 16; + blkdev->index = drive_get_index(IF_XEN, 0, blkdev->index); + if (blkdev->index == -1) { + /* setup via xenbus -> create new block driver instance */ + xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); + blkdev->bs = bdrv_new(blkdev->dev); + if (blkdev->bs) { + if (0 != bdrv_open2(blkdev->bs, blkdev->filename, qflags, + bdrv_find_format(blkdev->fileproto))) { + bdrv_delete(blkdev->bs); + blkdev->bs = NULL; + } + } + if (!blkdev->bs) + return -1; + } else { + /* setup via qemu cmdline -> already setup for us */ + xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n"); + blkdev->bs = drives_table[blkdev->index].bdrv; + } + blkdev->file_blk = BLOCK_SIZE; + blkdev->file_size = bdrv_getlength(blkdev->bs); + if (blkdev->file_size < 0) { + xen_be_printf(&blkdev->xendev, 1, "bdrv_getlength: %d (%s) | drv %s\n", + (int)blkdev->file_size, strerror(-blkdev->file_size), + blkdev->bs->drv ? blkdev->bs->drv->format_name : "-"); + blkdev->file_size = 0; + } + have_barriers = blkdev->bs->drv && blkdev->bs->drv->bdrv_flush ? 1 : 0; + + xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," + " size %" PRId64 " (%" PRId64 " MB)\n", + blkdev->type, blkdev->fileproto, blkdev->filename, + blkdev->file_size, blkdev->file_size >> 20); + + /* fill info */ + xenstore_write_be_int(&blkdev->xendev, "feature-barrier", have_barriers); + xenstore_write_be_int(&blkdev->xendev, "info", info); + xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); + xenstore_write_be_int(&blkdev->xendev, "sectors", + blkdev->file_size / blkdev->file_blk); + return 0; +} + +static int blk_connect(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + if (-1 == xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref)) + return -1; + if (-1 == xenstore_read_fe_int(&blkdev->xendev, "event-channel", + &blkdev->xendev.remote_port)) + return -1; + + blkdev->protocol = BLKIF_PROTOCOL_NATIVE; + if (blkdev->xendev.protocol) { + if (0 == strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32)) + blkdev->protocol = BLKIF_PROTOCOL_X86_32; + if (0 == strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64)) + blkdev->protocol = BLKIF_PROTOCOL_X86_64; + } + + blkdev->sring = xc_gnttab_map_grant_ref(blkdev->xendev.gnttabdev, + blkdev->xendev.dom, + blkdev->ring_ref, + PROT_READ | PROT_WRITE); + if (!blkdev->sring) + return -1; + blkdev->cnt_map++; + + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + { + blkif_sring_t *sring_native = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.native, sring_native, XC_PAGE_SIZE); + break; + } + case BLKIF_PROTOCOL_X86_32: + { + blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.x86_32, sring_x86_32, XC_PAGE_SIZE); + break; + } + case BLKIF_PROTOCOL_X86_64: + { + blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.x86_64, sring_x86_64, XC_PAGE_SIZE); + break; + } + } + + xen_be_bind_evtchn(&blkdev->xendev); + + xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, " + "remote port %d, local port %d\n", + blkdev->xendev.protocol, blkdev->ring_ref, + blkdev->xendev.remote_port, blkdev->xendev.local_port); + return 0; +} + +static void blk_disconnect(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + if (blkdev->bs) { + if (blkdev->index == -1) { + /* close/delete only if we created it ourself */ + bdrv_close(blkdev->bs); + bdrv_delete(blkdev->bs); + } + blkdev->bs = NULL; + } + xen_be_unbind_evtchn(&blkdev->xendev); + + if (blkdev->sring) { + xc_gnttab_munmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); + blkdev->cnt_map--; + blkdev->sring = NULL; + } +} + +static int blk_free(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + struct ioreq *ioreq; + + while (!LIST_EMPTY(&blkdev->freelist)) { + ioreq = LIST_FIRST(&blkdev->freelist); + LIST_REMOVE(ioreq, list); + qemu_free(ioreq); + } + + qemu_free(blkdev->params); + qemu_free(blkdev->mode); + qemu_bh_delete(blkdev->bh); + return 0; +} + +static void blk_event(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + qemu_bh_schedule(blkdev->bh); +} + +struct XenDevOps xen_blkdev_ops = { + .size = sizeof(struct XenBlkDev), + .flags = DEVOPS_FLAG_NEED_GNTDEV, + .alloc = blk_alloc, + .init = blk_init, + .connect = blk_connect, + .disconnect = blk_disconnect, + .event = blk_event, + .free = blk_free, +}; diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index fee794a..46f5df8 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -59,6 +59,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, xen_be_register("console", &xen_console_ops); xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); + xen_be_register("qdisk", &xen_blkdev_ops); /* setup framebuffer */ xen_init_display(xen_domid); diff --git a/sysemu.h b/sysemu.h index 3eab34b..7b356b3 100644 --- a/sysemu.h +++ b/sysemu.h @@ -127,7 +127,7 @@ extern unsigned int nb_prom_envs; #endif typedef enum { - IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO + IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN } BlockInterfaceType; typedef enum { diff --git a/vl.c b/vl.c index 76fca5a..edf9ea9 100644 --- a/vl.c +++ b/vl.c @@ -2364,7 +2364,10 @@ int drive_init(struct drive_opt *arg, int snapshot, void *opaque) } else if (!strcmp(buf, "virtio")) { type = IF_VIRTIO; max_devs = 0; - } else { + } else if (!strcmp(buf, "xen")) { + type = IF_XEN; + max_devs = 0; + } else { fprintf(stderr, "qemu: ''%s'' unsupported bus type ''%s''\n", str, buf); return -1; } @@ -2578,6 +2581,7 @@ int drive_init(struct drive_opt *arg, int snapshot, void *opaque) switch(type) { case IF_IDE: case IF_SCSI: + case IF_XEN: switch(media) { case MEDIA_DISK: if (cyls != 0) { -- 1.6.1.3 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-01 21:39 UTC
[Xen-devel] [PATCH 06/10] xen: add net backend driver.
This patch adds a network interface backend driver to qemu. It is a pure userspace implemention using the gntdev interface. It uses "qnet" as backend name in xenstore so it doesn''t interfere with the netback backend (aka "vnif"). The network backend is hooked into the corrosponding qemu vlan, i.e. vif 0 is hooked into vlan 0. To make the packages actually arrive somewhere you additionally have to link the vlan to the outside world using the usual qemu command line options such as "-net tap,...". Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen_backend.h | 1 + hw/xen_machine_pv.c | 1 + hw/xen_nic.c | 396 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 399 insertions(+), 1 deletions(-) create mode 100644 hw/xen_nic.c diff --git a/Makefile.target b/Makefile.target index b66ff27..4b071eb 100644 --- a/Makefile.target +++ b/Makefile.target @@ -565,7 +565,7 @@ endif # xen backend driver support XEN_OBJS := xen_machine_pv.o xen_backend.o -XEN_OBJS += xen_console.o xenfb.o xen_disk.o +XEN_OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.h b/hw/xen_backend.h index dd426dd..4e4be14 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -89,6 +89,7 @@ extern struct XenDevOps xen_console_ops; /* xen_console.c */ extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ extern struct XenDevOps xen_blkdev_ops; /* xen_disk.c */ +extern struct XenDevOps xen_netdev_ops; /* xen_nic.c */ void xen_init_display(int domid); diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 46f5df8..50fdd15 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -60,6 +60,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); xen_be_register("qdisk", &xen_blkdev_ops); + xen_be_register("qnic", &xen_netdev_ops); /* setup framebuffer */ xen_init_display(xen_domid); diff --git a/hw/xen_nic.c b/hw/xen_nic.c new file mode 100644 index 0000000..0c01859 --- /dev/null +++ b/hw/xen_nic.c @@ -0,0 +1,396 @@ +/* + * xen paravirt network card backend + * + * (c) Gerd Hoffmann <kraxel@redhat.com> + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <inttypes.h> +#include <fcntl.h> +#include <errno.h> +#include <pthread.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/wait.h> +#include <linux/if.h> +#include <linux/if_tun.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> +#include <xen/io/netif.h> + +#include "hw.h" +#include "net.h" +#include "qemu-char.h" +#include "xen_backend.h" + +/* ------------------------------------------------------------- */ + +struct XenNetDev { + struct XenDevice xendev; /* must be first */ + char *mac; + int tx_work; + int tx_ring_ref; + int rx_ring_ref; + struct netif_tx_sring *txs; + struct netif_rx_sring *rxs; + netif_tx_back_ring_t tx_ring; + netif_rx_back_ring_t rx_ring; + VLANClientState *vs; +}; + +/* ------------------------------------------------------------- */ + +static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, int8_t st) +{ + RING_IDX i = netdev->tx_ring.rsp_prod_pvt; + netif_tx_response_t *resp; + int notify; + + resp = RING_GET_RESPONSE(&netdev->tx_ring, i); + resp->id = txp->id; + resp->status = st; + +#if 0 + if (txp->flags & NETTXF_extra_info) + RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL; +#endif + + netdev->tx_ring.rsp_prod_pvt = ++i; + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify); + if (notify) + xen_be_send_notify(&netdev->xendev); + + if (i == netdev->tx_ring.req_cons) { + int more_to_do; + RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do); + if (more_to_do) + netdev->tx_work++; + } +} + +static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING_IDX end) +{ +#if 0 + /* + * Hmm, why netback fails everything in the ring? + * Should we do that even when not supporting SG and TSO? + */ + RING_IDX cons = netdev->tx_ring.req_cons; + + do { + make_tx_response(netif, txp, NETIF_RSP_ERROR); + if (cons >= end) + break; + txp = RING_GET_REQUEST(&netdev->tx_ring, cons++); + } while (1); + netdev->tx_ring.req_cons = cons; + netif_schedule_work(netif); + netif_put(netif); +#else + net_tx_response(netdev, txp, NETIF_RSP_ERROR); +#endif +} + +static void net_tx_packets(struct XenNetDev *netdev) +{ + netif_tx_request_t txreq; + RING_IDX rc, rp; + void *page; + void *tmpbuf = NULL; + + for (;;) { + rc = netdev->tx_ring.req_cons; + rp = netdev->tx_ring.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + while ((rc != rp)) { + if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) + break; + memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq)); + netdev->tx_ring.req_cons = ++rc; + +#if 1 + /* should not happen in theory, we don''t announce the * + * feature-{sg,gso,whatelse} flags in xenstore (yet?) */ + if (txreq.flags & NETTXF_extra_info) { + xen_be_printf(&netdev->xendev, 0, "FIXME: extra info flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_more_data) { + xen_be_printf(&netdev->xendev, 0, "FIXME: more data flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } +#endif + + if (txreq.size < 14) { + xen_be_printf(&netdev->xendev, 0, "bad packet size: %d\n", txreq.size); + net_tx_error(netdev, &txreq, rc); + continue; + } + + if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) { + xen_be_printf(&netdev->xendev, 0, "error: page crossing\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + + xen_be_printf(&netdev->xendev, 3, "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n", + txreq.gref, txreq.offset, txreq.size, txreq.flags, + (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", + (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", + (txreq.flags & NETTXF_more_data) ? " more_data" : "", + (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); + + page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + txreq.gref, PROT_READ); + if (NULL == page) { + xen_be_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n", + txreq.gref); + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_csum_blank) { + /* have read-only mapping -> can''t fill checksum in-place */ + if (!tmpbuf) + tmpbuf = malloc(PAGE_SIZE); + memcpy(tmpbuf, page + txreq.offset, txreq.size); + net_checksum_calculate(tmpbuf, txreq.size); + qemu_send_packet(netdev->vs, tmpbuf, txreq.size); + } else { + qemu_send_packet(netdev->vs, page + txreq.offset, txreq.size); + } + xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); + } + if (!netdev->tx_work) + break; + netdev->tx_work = 0; + } + free(tmpbuf); +} + +/* ------------------------------------------------------------- */ + +static void net_rx_response(struct XenNetDev *netdev, + netif_rx_request_t *req, int8_t st, + uint16_t offset, uint16_t size, + uint16_t flags) +{ + RING_IDX i = netdev->rx_ring.rsp_prod_pvt; + netif_rx_response_t *resp; + int notify; + + resp = RING_GET_RESPONSE(&netdev->rx_ring, i); + resp->offset = offset; + resp->flags = flags; + resp->id = req->id; + resp->status = (int16_t)size; + if (st < 0) + resp->status = (int16_t)st; + + xen_be_printf(&netdev->xendev, 3, "rx response: idx %d, status %d, flags 0x%x\n", + i, resp->status, resp->flags); + + netdev->rx_ring.rsp_prod_pvt = ++i; + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify); + if (notify) + xen_be_send_notify(&netdev->xendev); +} + +#define NET_IP_ALIGN 2 + +static int net_rx_ok(void *opaque) +{ + struct XenNetDev *netdev = opaque; + RING_IDX rc, rp; + + if (netdev->xendev.be_state != XenbusStateConnected) + return 0; + + rc = netdev->rx_ring.req_cons; + rp = netdev->rx_ring.sring->req_prod; + xen_rmb(); + + if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { + xen_be_printf(&netdev->xendev, 2, "%s: no rx buffers (%d/%d)\n", + __FUNCTION__, rc, rp); + return 0; + } + return 1; +} + +static void net_rx_packet(void *opaque, const uint8_t *buf, int size) +{ + struct XenNetDev *netdev = opaque; + netif_rx_request_t rxreq; + RING_IDX rc, rp; + void *page; + + if (netdev->xendev.be_state != XenbusStateConnected) + return; + + rc = netdev->rx_ring.req_cons; + rp = netdev->rx_ring.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { + xen_be_printf(&netdev->xendev, 2, "no buffer, drop packet\n"); + return; + } + if (size > XC_PAGE_SIZE - NET_IP_ALIGN) { + xen_be_printf(&netdev->xendev, 0, "packet too big (%d > %ld)", + size, XC_PAGE_SIZE - NET_IP_ALIGN); + return; + } + + memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); + netdev->rx_ring.req_cons = ++rc; + + page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + rxreq.gref, PROT_WRITE); + if (NULL == page) { + xen_be_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n", + rxreq.gref); + net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0); + return; + } + memcpy(page + NET_IP_ALIGN, buf, size); + xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0); +} + +/* ------------------------------------------------------------- */ + +static int net_init(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + VLANState *vlan; + + /* read xenstore entries */ + if (NULL == netdev->mac) + netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac"); + + /* do we have all we need? */ + if (NULL == netdev->mac) + return -1; + + vlan = qemu_find_vlan(netdev->xendev.dev); + netdev->vs = qemu_new_vlan_client(vlan, "xen", NULL, + net_rx_packet, net_rx_ok, netdev); + snprintf(netdev->vs->info_str, sizeof(netdev->vs->info_str), + "nic: xenbus vif macaddr=%s", netdev->mac); + + /* fill info */ + xenstore_write_be_int(&netdev->xendev, "feature-rx-copy", 1); + xenstore_write_be_int(&netdev->xendev, "feature-rx-flip", 0); + + return 0; +} + +static int net_connect(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + int rx_copy; + + if (-1 == xenstore_read_fe_int(&netdev->xendev, "tx-ring-ref", + &netdev->tx_ring_ref)) + return -1; + if (-1 == xenstore_read_fe_int(&netdev->xendev, "rx-ring-ref", + &netdev->rx_ring_ref)) + return 1; + if (-1 == xenstore_read_fe_int(&netdev->xendev, "event-channel", + &netdev->xendev.remote_port)) + return -1; + + if (-1 == xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy)) + rx_copy = 0; + if (0 == rx_copy) { + xen_be_printf(&netdev->xendev, 0, "frontend doesn''t support rx-copy.\n"); + return -1; + } + + netdev->txs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + netdev->tx_ring_ref, + PROT_READ | PROT_WRITE); + netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + netdev->rx_ring_ref, + PROT_READ | PROT_WRITE); + if (!netdev->txs || !netdev->rxs) + return -1; + BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE); + BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XC_PAGE_SIZE); + + xen_be_bind_evtchn(&netdev->xendev); + + xen_be_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, " + "remote port %d, local port %d\n", + netdev->tx_ring_ref, netdev->rx_ring_ref, + netdev->xendev.remote_port, netdev->xendev.local_port); + return 0; +} + +static void net_disconnect(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + + xen_be_unbind_evtchn(&netdev->xendev); + + if (netdev->txs) { + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); + netdev->txs = NULL; + } + if (netdev->rxs) { + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1); + netdev->rxs = NULL; + } + if (netdev->vs) { + qemu_del_vlan_client(netdev->vs); + netdev->vs = NULL; + } +} + +static void net_event(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + net_tx_packets(netdev); +} + +/* ------------------------------------------------------------- */ + +struct XenDevOps xen_netdev_ops = { + .size = sizeof(struct XenNetDev), + .flags = DEVOPS_FLAG_NEED_GNTDEV, + .init = net_init, + .connect = net_connect, + .event = net_event, + .disconnect = net_disconnect, +}; -- 1.6.1.3 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-01 21:39 UTC
[Xen-devel] [PATCH 07/10] xen: blk & nic configuration via cmd line.
This patch makes qemu create backend and frontend device entries in xenstore for devices configured on the command line. It will use qdisk and qnic backend names, so the qemu internal backends will be used. Disks can be created using -drive if=xen,file=... Nics can be created using -net nic,macaddr=... Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen_backend.c | 1 + hw/xen_backend.h | 8 +++ hw/xen_devconfig.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_machine_pv.c | 19 +++++++ 5 files changed, 174 insertions(+), 1 deletions(-) create mode 100644 hw/xen_devconfig.c diff --git a/Makefile.target b/Makefile.target index 4b071eb..8bae243 100644 --- a/Makefile.target +++ b/Makefile.target @@ -564,7 +564,7 @@ LIBS += $(CONFIG_BLUEZ_LIBS) endif # xen backend driver support -XEN_OBJS := xen_machine_pv.o xen_backend.o +XEN_OBJS := xen_machine_pv.o xen_backend.o xen_devconfig.o XEN_OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) diff --git a/hw/xen_backend.c b/hw/xen_backend.c index 2678dfa..2bd4433 100644 --- a/hw/xen_backend.c +++ b/hw/xen_backend.c @@ -45,6 +45,7 @@ /* public */ int xen_xc; struct xs_handle *xenstore = NULL; +const char *xen_protocol; /* private */ static TAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = TAILQ_HEAD_INITIALIZER(xendevs); diff --git a/hw/xen_backend.h b/hw/xen_backend.h index 4e4be14..7f1804e 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -3,6 +3,8 @@ #include "xen_common.h" #include "sysemu.h" +#include "net.h" +#include "block_int.h" /* ------------------------------------------------------------- */ @@ -56,6 +58,7 @@ struct XenDevice { /* variables */ extern int xen_xc; extern struct xs_handle *xenstore; +extern const char *xen_protocol; /* xenstore helper functions */ int xenstore_write_str(const char *base, const char *node, const char *val); @@ -93,4 +96,9 @@ extern struct XenDevOps xen_netdev_ops; /* xen_nic.c */ void xen_init_display(int domid); +/* configuration (aka xenbus setup) */ +void xen_config_cleanup(void); +int xen_config_dev_blk(DriveInfo *disk); +int xen_config_dev_nic(NICInfo *nic); + #endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c new file mode 100644 index 0000000..0ef8012 --- /dev/null +++ b/hw/xen_devconfig.c @@ -0,0 +1,145 @@ +#include "xen_backend.h" + +/* ------------------------------------------------------------- */ + +struct xs_dirs { + char *xs_dir; + TAILQ_ENTRY(xs_dirs) list; +}; +static TAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup = TAILQ_HEAD_INITIALIZER(xs_cleanup); + +static void xen_config_cleanup_dir(char *dir) +{ + struct xs_dirs *d; + + d = qemu_malloc(sizeof(*d)); + if (!d) + return; + d->xs_dir = dir; + TAILQ_INSERT_TAIL(&xs_cleanup, d, list); +} + +void xen_config_cleanup(void) +{ + struct xs_dirs *d; + + fprintf(stderr, "xen be: %s\n", __FUNCTION__); + TAILQ_FOREACH(d, &xs_cleanup, list) { + xs_rm(xenstore, 0, d->xs_dir); + } +} + +/* ------------------------------------------------------------- */ + +static int xen_config_dev_mkdir(char *dev, int p) +{ + struct xs_permissions perms[2] = {{ + .id = 0, /* set owner: dom0 */ + },{ + .id = xen_domid, + .perms = p, + }}; + + if (!xs_mkdir(xenstore, 0, dev)) { + fprintf(stderr, "xs_mkdir %s: failed\n", dev); + return -1; + } + xen_config_cleanup_dir(qemu_strdup(dev)); + + if (!xs_set_permissions(xenstore, 0, dev, perms, 2)) { + fprintf(stderr, "%s: xs_set_permissions failed\n", __FUNCTION__); + return -1; + } + return 0; +} + +static int xen_config_dev_dirs(const char *ftype, const char *btype, int vdev, + char *fe, char *be, int len) +{ + char *dom; + + dom = xs_get_domain_path(xenstore, xen_domid); + snprintf(fe, len, "%s/device/%s/%d", dom, ftype, vdev); + free(dom); + + dom = xs_get_domain_path(xenstore, 0); + snprintf(be, len, "%s/backend/%s/%d/%d", dom, btype, xen_domid, vdev); + free(dom); + + xen_config_dev_mkdir(fe, XS_PERM_READ | XS_PERM_WRITE); + xen_config_dev_mkdir(be, XS_PERM_READ); + return 0; +} + +static int xen_config_dev_all(char *fe, char *be) +{ + /* frontend */ + if (xen_protocol) + xenstore_write_str(fe, "protocol", xen_protocol); + + xenstore_write_int(fe, "state", XenbusStateInitialising); + xenstore_write_int(fe, "backend-id", 0); + xenstore_write_str(fe, "backend", be); + + /* backend */ + xenstore_write_str(be, "domain", qemu_name ? qemu_name : "no-name"); + xenstore_write_int(be, "online", 1); + xenstore_write_int(be, "state", XenbusStateInitialising); + xenstore_write_int(be, "frontend-id", xen_domid); + xenstore_write_str(be, "frontend", fe); + + return 0; +} + +/* ------------------------------------------------------------- */ + +int xen_config_dev_blk(DriveInfo *disk) +{ + char fe[256], be[256]; + int vdev = 202 * 256 + 16 * disk->unit; + int cdrom = disk->bdrv->type == BDRV_TYPE_CDROM; + const char *devtype = cdrom ? "cdrom" : "disk"; + const char *mode = cdrom ? "r" : "w"; + + snprintf(disk->bdrv->device_name, sizeof(disk->bdrv->device_name), + "xvd%c", ''a'' + disk->unit); + fprintf(stderr, "xen be: config disk %d [%s]: %s\n", + disk->unit, disk->bdrv->device_name, disk->bdrv->filename); + xen_config_dev_dirs("vbd", "qdisk", vdev, fe, be, sizeof(fe)); + + /* frontend */ + xenstore_write_int(fe, "virtual-device", vdev); + xenstore_write_str(fe, "device-type", devtype); + + /* backend */ + xenstore_write_str(be, "dev", disk->bdrv->device_name); + xenstore_write_str(be, "type", "file"); + xenstore_write_str(be, "params", disk->bdrv->filename); + xenstore_write_str(be, "mode", mode); + + /* common stuff */ + return xen_config_dev_all(fe, be); +} + +int xen_config_dev_nic(NICInfo *nic) +{ + char fe[256], be[256]; + char mac[20]; + + snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", + nic->macaddr[0], nic->macaddr[1], nic->macaddr[2], + nic->macaddr[3], nic->macaddr[4], nic->macaddr[5]); + fprintf(stderr, "xen be: config nic %d: mac=\"%s\"\n", nic->vlan->id, mac); + xen_config_dev_dirs("vif", "qnic", nic->vlan->id, fe, be, sizeof(fe)); + + /* frontend */ + xenstore_write_int(fe, "handle", nic->vlan->id); + xenstore_write_str(fe, "mac", mac); + + /* backend */ + xenstore_write_int(be, "handle", nic->vlan->id); + xenstore_write_str(be, "mac", mac); + + /* common stuff */ + return xen_config_dev_all(fe, be); +} diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 50fdd15..8c471c2 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -39,6 +39,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, const char *cpu_model) { CPUState *env; + int i, index; /* Initialize a dummy CPU */ if (cpu_model == NULL) { @@ -62,6 +63,24 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, xen_be_register("qdisk", &xen_blkdev_ops); xen_be_register("qnic", &xen_netdev_ops); + /* configure disks */ + for (i = 0; i < 16; i++) { + index = drive_get_index(IF_XEN, 0, i); + if (index == -1) + continue; + xen_config_dev_blk(drives_table + index); + } + + /* configure nics */ + for (i = 0; i < nb_nics; i++) { + if (!nd_table[i].model || 0 != strcmp(nd_table[i].model, "xen")) + continue; + xen_config_dev_nic(nd_table + i); + } + + /* config cleanup hook */ + atexit(xen_config_cleanup); + /* setup framebuffer */ xen_init_display(xen_domid); } -- 1.6.1.3 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
This adds domain building support for paravirtual domains to qemu. This allows booting xen guests directly with qemu, without Xend and the management stack. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- configure | 2 +- hw/xen_backend.h | 3 + hw/xen_devconfig.c | 29 +++++ hw/xen_domainbuild.c | 293 ++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_domainbuild.h | 13 +++ hw/xen_machine_pv.c | 19 ++++ 7 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 hw/xen_domainbuild.c create mode 100644 hw/xen_domainbuild.h diff --git a/Makefile.target b/Makefile.target index 8bae243..626a846 100644 --- a/Makefile.target +++ b/Makefile.target @@ -564,7 +564,7 @@ LIBS += $(CONFIG_BLUEZ_LIBS) endif # xen backend driver support -XEN_OBJS := xen_machine_pv.o xen_backend.o xen_devconfig.o +XEN_OBJS := xen_machine_pv.o xen_backend.o xen_devconfig.o xen_domainbuild.o XEN_OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) diff --git a/configure b/configure index 212f2e9..9e7738e 100755 --- a/configure +++ b/configure @@ -1518,7 +1518,7 @@ if test "$bluez" = "yes" ; then echo "#define CONFIG_BLUEZ 1" >> $config_h fi if test "$xen" = "yes" ; then - echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak + echo "XEN_LIBS=-lxenstore -lxenctrl -lxenguest" >> $config_mak fi if test "$aio" = "yes" ; then echo "#define CONFIG_AIO 1" >> $config_h diff --git a/hw/xen_backend.h b/hw/xen_backend.h index 7f1804e..4dbfdb4 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -100,5 +100,8 @@ void xen_init_display(int domid); void xen_config_cleanup(void); int xen_config_dev_blk(DriveInfo *disk); int xen_config_dev_nic(NICInfo *nic); +int xen_config_dev_vfb(int vdev, const char *type); +int xen_config_dev_vkbd(int vdev); +int xen_config_dev_console(int vdev); #endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c index 0ef8012..1417e71 100644 --- a/hw/xen_devconfig.c +++ b/hw/xen_devconfig.c @@ -143,3 +143,32 @@ int xen_config_dev_nic(NICInfo *nic) /* common stuff */ return xen_config_dev_all(fe, be); } + +int xen_config_dev_vfb(int vdev, const char *type) +{ + char fe[256], be[256]; + + xen_config_dev_dirs("vfb", "vfb", vdev, fe, be, sizeof(fe)); + + /* backend */ + xenstore_write_str(be, "type", type); + + /* common stuff */ + return xen_config_dev_all(fe, be); +} + +int xen_config_dev_vkbd(int vdev) +{ + char fe[256], be[256]; + + xen_config_dev_dirs("vkbd", "vkbd", vdev, fe, be, sizeof(fe)); + return xen_config_dev_all(fe, be); +} + +int xen_config_dev_console(int vdev) +{ + char fe[256], be[256]; + + xen_config_dev_dirs("console", "console", vdev, fe, be, sizeof(fe)); + return xen_config_dev_all(fe, be); +} diff --git a/hw/xen_domainbuild.c b/hw/xen_domainbuild.c new file mode 100644 index 0000000..9814b8d --- /dev/null +++ b/hw/xen_domainbuild.c @@ -0,0 +1,293 @@ +#include <signal.h> +#include "xen_backend.h" +#include "xen_domainbuild.h" +#include "sysemu.h" +#include "qemu-timer.h" + +#include <xenguest.h> + +static int xenstore_domain_mkdir(char *path) +{ + struct xs_permissions perms_ro[] = {{ + .id = 0, /* set owner: dom0 */ + },{ + .id = xen_domid, + .perms = XS_PERM_READ, + }}; + struct xs_permissions perms_rw[] = {{ + .id = 0, /* set owner: dom0 */ + },{ + .id = xen_domid, + .perms = XS_PERM_READ | XS_PERM_WRITE, + }}; + const char *writable[] = { "device", "control", "error", NULL }; + char subpath[256]; + int i; + + if (!xs_mkdir(xenstore, 0, path)) { + fprintf(stderr, "%s: xs_mkdir %s: failed\n", __FUNCTION__, path); + return -1; + } + if (!xs_set_permissions(xenstore, 0, path, perms_ro, 2)) { + fprintf(stderr, "%s: xs.set_permissions failed\n", __FUNCTION__); + return -1; + } + + for (i = 0; writable[i]; i++) { + snprintf(subpath, sizeof(subpath), "%s/%s", path, writable[i]); + if (!xs_mkdir(xenstore, 0, subpath)) { + fprintf(stderr, "%s: xs.mkdir %s: failed\n", __FUNCTION__, subpath); + return -1; + } + if (!xs_set_permissions(xenstore, 0, subpath, perms_rw, 2)) { + fprintf(stderr, "%s: xs.set_permissions failed\n", __FUNCTION__); + return -1; + } + } + return 0; +} + +int xenstore_domain_init1(const char *kernel, const char *ramdisk, + const char *cmdline) +{ + char *dom, uuid_string[42], vm[256], path[256]; + int i; + + snprintf(uuid_string, sizeof(uuid_string), UUID_FMT, + qemu_uuid[0], qemu_uuid[1], qemu_uuid[2], qemu_uuid[3], + qemu_uuid[4], qemu_uuid[5], qemu_uuid[6], qemu_uuid[7], + qemu_uuid[8], qemu_uuid[9], qemu_uuid[10], qemu_uuid[11], + qemu_uuid[12], qemu_uuid[13], qemu_uuid[14], qemu_uuid[15]); + dom = xs_get_domain_path(xenstore, xen_domid); + snprintf(vm, sizeof(vm), "/vm/%s", uuid_string); + + xenstore_domain_mkdir(dom); + + xenstore_write_str(vm, "image/ostype", "linux"); + if (kernel) + xenstore_write_str(vm, "image/kernel", kernel); + if (ramdisk) + xenstore_write_str(vm, "image/ramdisk", ramdisk); + if (cmdline) + xenstore_write_str(vm, "image/cmdline", cmdline); + + /* name + id */ + xenstore_write_str(vm, "name", qemu_name ? qemu_name : "no-name"); + xenstore_write_str(vm, "uuid", uuid_string); + xenstore_write_str(dom, "name", qemu_name ? qemu_name : "no-name"); + xenstore_write_int(dom, "domid", xen_domid); + xenstore_write_str(dom, "vm", vm); + + /* memory */ + xenstore_write_int(dom, "memory/target", ram_size >> 10); // kB + xenstore_write_int(vm, "memory", ram_size >> 20); // MB + xenstore_write_int(vm, "maxmem", ram_size >> 20); // MB + + /* cpus */ + for (i = 0; i < smp_cpus; i++) { + snprintf(path, sizeof(path), "cpu/%d/availability",i); + xenstore_write_str(dom, path, "online"); + } + xenstore_write_int(vm, "vcpu_avail", smp_cpus); + xenstore_write_int(vm, "vcpus", smp_cpus); + + /* vnc password */ + xenstore_write_str(vm, "vncpassword", "" /* FIXME */); + + free(dom); + return 0; +} + +int xenstore_domain_init2(int xenstore_port, int xenstore_mfn, + int console_port, int console_mfn) +{ + char *dom; + + dom = xs_get_domain_path(xenstore, xen_domid); + + /* signal new domain */ + xs_introduce_domain(xenstore, + xen_domid, + xenstore_mfn, + xenstore_port); + + /* xenstore */ + xenstore_write_int(dom, "store/ring-ref", xenstore_mfn); + xenstore_write_int(dom, "store/port", xenstore_port); + + /* console */ + xenstore_write_str(dom, "console/type", "ioemu"); + xenstore_write_int(dom, "console/limit", 128 * 1024); + xenstore_write_int(dom, "console/ring-ref", console_mfn); + xenstore_write_int(dom, "console/port", console_port); + xen_config_dev_console(0); + + free(dom); + return 0; +} + +/* ------------------------------------------------------------- */ + +static QEMUTimer *xen_poll; + +/* check domain state once per second */ +static void xen_domain_poll(void *opaque) +{ + struct xc_dominfo info; + int rc; + + rc = xc_domain_getinfo(xen_xc, xen_domid, 1, &info); + if ((1 != rc) || (info.domid != xen_domid)) { + fprintf(stderr, "xen: domain %d is gone\n", xen_domid); + goto quit; + } + if (info.dying) { + fprintf(stderr, "xen: domain %d is dying (%s%s)\n", xen_domid, + info.crashed ? "crashed" : "", + info.shutdown ? "shutdown" : ""); + goto quit; + } + + qemu_mod_timer(xen_poll, qemu_get_clock(rt_clock) + 1000); + return; + +quit: + qemu_system_shutdown_request(); + return; +} + +static void xen_domain_watcher(void) +{ + int qemu_running = 1; + int fd[2], i, rc; + char byte; + + pipe(fd); + if (0 != fork()) + return; /* not child */ + + /* close all file handles, except stdio/out/err, + * our watch pipe and the xen interface handle */ + for (i = 3; i < 256; i++) { + if (i == fd[0]) + continue; + if (i == xen_xc) + continue; + close(i); + } + + /* ignore term signals */ + signal(SIGINT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + + /* wait for qemu exiting */ + while (qemu_running) { + rc = read(fd[0], &byte, 1); + switch (rc) { + case -1: + if (EINTR == errno) + continue; + fprintf(stderr, "%s: Huh? read error: %s\n", __FUNCTION__, strerror(errno)); + qemu_running = 0; + break; + case 0: + /* EOF -> qemu exited */ + qemu_running = 0; + break; + default: + fprintf(stderr, "%s: Huh? data on the watch pipe?\n", __FUNCTION__); + break; + } + } + + /* cleanup */ + fprintf(stderr, "%s: destroy domain %d\n", __FUNCTION__, xen_domid); + xc_domain_destroy(xen_xc, xen_domid); + _exit(0); +} + +/* normal cleanup */ +static void xen_domain_cleanup(void) +{ + char *dom; + + dom = xs_get_domain_path(xenstore, xen_domid); + if (dom) { + xs_rm(xenstore, 0, dom); + free(dom); + } + xs_release_domain(xenstore, xen_domid); +} + +int xen_domain_build_pv(const char *kernel, const char *ramdisk, + const char *cmdline) +{ + uint32_t ssidref = 0; + uint32_t flags = 0; + xen_domain_handle_t uuid; + unsigned int xenstore_port = 0, console_port = 0; + unsigned long xenstore_mfn = 0, console_mfn = 0; + int rc; + + memcpy(uuid, qemu_uuid, sizeof(uuid)); + rc = xc_domain_create(xen_xc, ssidref, uuid, flags, &xen_domid); + if (rc < 0) { + fprintf(stderr, "xen: xc_domain_create() failed\n"); + goto err; + } + fprintf(stderr, "xen: created domain %d\n", xen_domid); + atexit(xen_domain_cleanup); + xen_domain_watcher(); + + xenstore_domain_init1(kernel, ramdisk, cmdline); + + rc = xc_domain_max_vcpus(xen_xc, xen_domid, smp_cpus); + if (rc < 0) { + fprintf(stderr, "xen: xc_domain_max_vcpus() failed\n"); + goto err; + } + +#if 0 + rc = xc_domain_setcpuweight(xen_xc, xen_domid, 256); + if (rc < 0) { + fprintf(stderr, "xen: xc_domain_setcpuweight() failed\n"); + goto err; + } +#endif + + rc = xc_domain_setmaxmem(xen_xc, xen_domid, ram_size >> 10); + if (rc < 0) { + fprintf(stderr, "xen: xc_domain_setmaxmem() failed\n"); + goto err; + } + + xenstore_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); + console_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); + + rc = xc_linux_build(xen_xc, xen_domid, ram_size >> 20, + kernel, ramdisk, cmdline, + 0, flags, + xenstore_port, &xenstore_mfn, + console_port, &console_mfn); + if (rc < 0) { + fprintf(stderr, "xen: xc_linux_build() failed\n"); + goto err; + } + + xenstore_domain_init2(xenstore_port, xenstore_mfn, + console_port, console_mfn); + + fprintf(stderr, "xen: unpausing domain %d\n", xen_domid); + rc = xc_domain_unpause(xen_xc, xen_domid); + if (rc < 0) { + fprintf(stderr, "xen: xc_domain_unpause() failed\n"); + goto err; + } + + xen_poll = qemu_new_timer(rt_clock, xen_domain_poll, NULL); + qemu_mod_timer(xen_poll, qemu_get_clock(rt_clock) + 1000); + return 0; + +err: + return -1; +} diff --git a/hw/xen_domainbuild.h b/hw/xen_domainbuild.h new file mode 100644 index 0000000..dea0121 --- /dev/null +++ b/hw/xen_domainbuild.h @@ -0,0 +1,13 @@ +#ifndef QEMU_HW_XEN_DOMAINBUILD_H +#define QEMU_HW_XEN_DOMAINBUILD_H 1 + +#include "xen_common.h" + +int xenstore_domain_init1(const char *kernel, const char *ramdisk, + const char *cmdline); +int xenstore_domain_init2(int xenstore_port, int xenstore_mfn, + int console_port, int console_mfn); +int xen_domain_build_pv(const char *kernel, const char *ramdisk, + const char *cmdline); + +#endif /* QEMU_HW_XEN_DOMAINBUILD_H */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 8c471c2..5607127 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -27,6 +27,7 @@ #include "sysemu.h" #include "boards.h" #include "xen_backend.h" +#include "xen_domainbuild.h" uint32_t xen_domid; enum xen_mode xen_mode = XEN_EMULATE; @@ -57,6 +58,24 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); exit(1); } + + switch (xen_mode) { + case XEN_ATTACH: + /* nothing to do, xend handles everything */ + break; + case XEN_CREATE: + if (xen_domain_build_pv(kernel_filename, initrd_filename, + kernel_cmdline) < 0) { + fprintf(stderr, "xen pv domain creation failed\n"); + exit(1); + } + break; + case XEN_EMULATE: + fprintf(stderr, "xen emulation not implemented (yet)\n"); + exit(1); + break; + } + xen_be_register("console", &xen_console_ops); xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); -- 1.6.1.3 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- vl.c | 15 ++++----------- 1 files changed, 4 insertions(+), 11 deletions(-) diff --git a/vl.c b/vl.c index edf9ea9..243fc0f 100644 --- a/vl.c +++ b/vl.c @@ -4149,23 +4149,16 @@ static void select_vgahw (const char *p) { const char *opts; + cirrus_vga_enabled = 0; + std_vga_enabled = 0; + vmsvga_enabled = 0; if (strstart(p, "std", &opts)) { std_vga_enabled = 1; - cirrus_vga_enabled = 0; - vmsvga_enabled = 0; } else if (strstart(p, "cirrus", &opts)) { cirrus_vga_enabled = 1; - std_vga_enabled = 0; - vmsvga_enabled = 0; } else if (strstart(p, "vmware", &opts)) { - cirrus_vga_enabled = 0; - std_vga_enabled = 0; vmsvga_enabled = 1; - } else if (strstart(p, "none", &opts)) { - cirrus_vga_enabled = 0; - std_vga_enabled = 0; - vmsvga_enabled = 0; - } else { + } else if (!strstart(p, "none", &opts)) { invalid_vga: fprintf(stderr, "Unknown vga type: %s\n", p); exit(1); -- 1.6.1.3 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-01 21:39 UTC
[Xen-devel] [PATCH 10/10] xen: add -vga xenfb option, configure xenfb
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- hw/xen_machine_pv.c | 6 ++++++ qemu-options.hx | 2 +- sysemu.h | 1 + vl.c | 4 ++++ 4 files changed, 12 insertions(+), 1 deletions(-) diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 5607127..0e12bfe 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -82,6 +82,12 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, xen_be_register("qdisk", &xen_blkdev_ops); xen_be_register("qnic", &xen_netdev_ops); + /* configure framebuffer */ + if (xenfb_enabled) { + xen_config_dev_vfb(0, "vnc"); + xen_config_dev_vkbd(0); + } + /* configure disks */ for (i = 0; i < 16; i++) { index = drive_get_index(IF_XEN, 0, i); diff --git a/qemu-options.hx b/qemu-options.hx index 895fa89..79c71ac 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -452,7 +452,7 @@ Rotate graphical output 90 deg left (only PXA LCD). ETEXI DEF("vga", HAS_ARG, QEMU_OPTION_vga, - "-vga [std|cirrus|vmware|none]\n" + "-vga [std|cirrus|vmware|xenfb|none]\n" " select video card type\n") STEXI @item -vga @var{type} diff --git a/sysemu.h b/sysemu.h index 7b356b3..e22612b 100644 --- a/sysemu.h +++ b/sysemu.h @@ -87,6 +87,7 @@ extern int bios_size; extern int cirrus_vga_enabled; extern int std_vga_enabled; extern int vmsvga_enabled; +extern int xenfb_enabled; extern int graphic_width; extern int graphic_height; extern int graphic_depth; diff --git a/vl.c b/vl.c index 243fc0f..1d9ea73 100644 --- a/vl.c +++ b/vl.c @@ -215,6 +215,7 @@ static int rtc_date_offset = -1; /* -1 means no change */ int cirrus_vga_enabled = 1; int std_vga_enabled = 0; int vmsvga_enabled = 0; +int xenfb_enabled = 0; #ifdef TARGET_SPARC int graphic_width = 1024; int graphic_height = 768; @@ -4152,12 +4153,15 @@ static void select_vgahw (const char *p) cirrus_vga_enabled = 0; std_vga_enabled = 0; vmsvga_enabled = 0; + xenfb_enabled = 0; if (strstart(p, "std", &opts)) { std_vga_enabled = 1; } else if (strstart(p, "cirrus", &opts)) { cirrus_vga_enabled = 1; } else if (strstart(p, "vmware", &opts)) { vmsvga_enabled = 1; + } else if (strstart(p, "xenfb", &opts)) { + xenfb_enabled = 1; } else if (!strstart(p, "none", &opts)) { invalid_vga: fprintf(stderr, "Unknown vga type: %s\n", p); -- 1.6.1.3 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2009-Apr-01 23:41 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support.
Gerd Hoffmann wrote:> Hi, > > It''s been a while, but after a looooooooooooong time we''ve (hopefully) > finally sorted the merging issues with qemu-xen. So here is a fresh > version of the xen support for qemu patch series. Short overview > (individual patches have longer descriptions): >Have these made their way into qemu-xen-unstable? If not, has Ian at least Acked them? Regards, Anthony Liguori> #1 - groundwork for xen support (makefiles, configure, ...). > #2 - backend driver core (common code used by all backends). > #3 - add console backend driver. > #4 - add framebuffer backend driver. > > With these four patches in place upstream qemu is functional aequivalent > to qemu-xen for paravirtual guests. The patches are merged into > qemu-xen already, with the exception of a few framebuffer bits which got > hold back due to displaystate work. > > #5 - add block device backend driver. > #6 - add net backend driver. > > These two patches add backend drivers for disk and network to qemu. > > #7 - blk & nic configuration via cmd line. > #8 - pv domain builder. > #9 - simplify vga selection > #10 - add -vga xenfb option, configure xenfb > > These patches add support to qemu for creating xen pv guests. That way > one can run xen guests without xend. Patch #8 is the domain builder > code itself. The other patches add support for configuring devices (fb, > disk, nic) via command line. > > cheers, > Gerd > > > >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Ian Jackson
2009-Apr-02 15:32 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support.
Anthony Liguori writes ("Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support."):> Gerd Hoffmann wrote: > > It''s been a while, but after a looooooooooooong time we''ve (hopefully) > > finally sorted the merging issues with qemu-xen. So here is a fresh > > version of the xen support for qemu patch series. Short overview > > (individual patches have longer descriptions):Well done and thanks for persisting.> Have these made their way into qemu-xen-unstable? If not, has Ian at > least Acked them?Some but not all of these are in qemu-xen-unstable. Not all of Gerd''s patches ought to go via qemu-xen-unstable - some should go direct. I''ve just merged up again from qemu-upstream at the 0.10.0 branch point so that qemu-xen-unstable staging is now reasonably recent. After I have fixed the resulting breakage and got that code through our tests into the main qemu-xen-untable we (Xen) will be in freeze and I''ll have some time to look at Gerd''s patches and comment and/or ack them individually. So please wait just a few more days if that''s not too inconvenient. I''m sorry this process is so slow but the massively improved rate of qemu development (yay!) has meant that we have been playing catch-up quite a bit. (Often, resolving conflicts between changes in upstream which are very similar but not identical to ones in our tree, so happily resulting in me being able to drop changes entirely.) Ian. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Christoph Hellwig
2009-Apr-02 17:02 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 05/10] xen: add block device backend driver.
On Wed, Apr 01, 2009 at 11:39:37PM +0200, Gerd Hoffmann wrote:> +static void inline blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_request_t *src) > +{> +static void inline blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_request_t *src) > +{I think you''d be better of moving them to the .c file as normal static function and leave the inlining decisions to the compiler.> + > +/* > + * FIXME: the code is designed to handle multiple outstanding > + * requests, which isn''t used right now. Plan is to > + * switch over to the aio block functions once they got > + * vector support. > + */We already have bdrv_aio_readv/writev which currently linearize the buffer underneath. Hopefully Anthony will have commited the patch to implement the real one while I''m writing this, too :) After those patches bdrv_aio_read/write will be gone so this code won''t compile anymore, too.> +static int ioreq_runio_qemu_aio(struct ioreq *ioreq) > +{ > + struct XenBlkDev *blkdev = ioreq->blkdev; > + int i, len = 0; > + off_t pos; > + > + if (-1 == ioreq_map(ioreq)) > + goto err; > + > + ioreq->aio_inflight++; > + if (ioreq->presync) > + bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */ > + > + switch (ioreq->req.operation) { > + case BLKIF_OP_READ: > + pos = ioreq->start; > + for (i = 0; i < ioreq->vecs; i++) { > + ioreq->aio_inflight++; > + bdrv_aio_read(blkdev->bs, pos / BLOCK_SIZE, > + ioreq->vec[i].iov_base, > + ioreq->vec[i].iov_len / BLOCK_SIZE, > + qemu_aio_complete, ioreq); > + len += ioreq->vec[i].iov_len; > + pos += ioreq->vec[i].iov_len; > + }hdrv_flush doesn''t actually empty the aio queues but only issues a fsync. So we could still re-order requeuests around the barrier with this implementation. I will soon submit a real block-layer level barrier implementation that just allows to flag a bdrv_aio_read/write request as barrier and deal with this under the hood. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-03 19:58 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 01/10] xen: groundwork for xen support
Blue Swirl wrote:> On 4/2/09, Gerd Hoffmann <kraxel@redhat.com> wrote: >> - configure script and build system changes. >> - wind up new machine type. >> - add -xen-* command line options. >> >> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > >> + $(XEN_OBJS) : CFLAGS += -Wall -Wmissing-prototypes -Wstrict-prototypes > > This should not be necessary, we already use all these flags.Great, will drop this.> >> +/* >> + * Local variables: >> + * indent-tabs-mode: nil >> + * c-indent-level: 4 >> + * c-basic-offset: 4 >> + * tab-width: 4 >> + * End: >> + */ > > Please remove.Yep. cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-03 19:59 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 02/10] xen: backend driver core
Blue Swirl wrote:> On 4/2/09, Gerd Hoffmann <kraxel@redhat.com> wrote: >> This patch adds infrastructure for xen backend drivers living in qemu, >> so drivers don''t need to implement common stuff on their own. It''s >> mostly xenbus management stuff: some functions to access xentore, >> setting up xenstore watches, callbacks on device discovery and state >> changes, handle event channel, ... >> >> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > >> + * You should have received a copy of the GNU General Public License >> + * along with this program; if not, write to the Free Software >> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > > The address is now 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.Will fix.>> + if (NULL == node || 0 == strcmp(node, "online")) { >> + if (-1 == xenstore_read_be_int(xendev, "online", &xendev->online)) > > This style is alien.Oops. Still one of them. I though I''ve killed them all, will check again. cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-03 20:11 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 05/10] xen: add block device backend driver.
Christoph Hellwig wrote:> On Wed, Apr 01, 2009 at 11:39:37PM +0200, Gerd Hoffmann wrote: >> +static void inline blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_request_t *src) >> +{ > >> +static void inline blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_request_t *src) >> +{ > > I think you''d be better of moving them to the .c file as normal static > function and leave the inlining decisions to the compiler.The .h file is a straight copy, thats why I left it as is. I can cut&paste it into the source file as well, I don''t care that much.>> + >> +/* >> + * FIXME: the code is designed to handle multiple outstanding >> + * requests, which isn''t used right now. Plan is to >> + * switch over to the aio block functions once they got >> + * vector support. >> + */ > > We already have bdrv_aio_readv/writev which currently linearize the > buffer underneath.Right, will adapt and kill the FIXME.> Hopefully Anthony will have commited the patch to > implement the real one while I''m writing this, too :)Linus committed the preadv syscall patches too (abi is not final yet though).>> + if (ioreq->presync) >> + bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */> hdrv_flush doesn''t actually empty the aio queues but only issues > a fsync. So we could still re-order requeuests around the barrier > with this implementation.Thats why there is a FIXME ...> I will soon submit a real block-layer level > barrier implementation that just allows to flag a bdrv_aio_read/write > request as barrier and deal with this under the hood.Great. cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-03 20:24 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support.
Laurent Vivier wrote:> Le mercredi 01 avril 2009 à 23:39 +0200, Gerd Hoffmann a écrit : >> Hi, > > Hi, > >> It''s been a while, but after a looooooooooooong time we''ve (hopefully) >> finally sorted the merging issues with qemu-xen. So here is a fresh >> version of the xen support for qemu patch series. Short overview >> (individual patches have longer descriptions): > > Could you explain how to use it. > > Do you need a xend and xenstored running on the host ?Depends ;) Long term plan is to have three ways of supporting xen guests in qemu (pv only for now): #1: Xen emulation (aka xenner). No code for this is submitted (yet). That will allow running xen kernels in qemu. No xend, no xenstored needed. Also no Xen hypervisor. #2: Make qemu attach to an existing domain. That is the way qemu-dm (xenified qemu version) works right now: xend creates the domain, and qemu attaches to it to provide console and framebuffer backend. Obviously needs both xend and xenstored. #3: Make qemu create xen domains. xenstored is still needed then. You can run without xend though. #2 works with the first 4 patches applied. #3 works with all patches applied. #1 needs more patches which are bitrotting in my git repo right now. I''ll dig them out once this patchset is merged. cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Avi Kivity
2009-Apr-05 08:48 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support.
Alexander Graf wrote:> > One idea I had for full virtualization in a Xen environment would be > an PV vmenter/vmexit framework - either by implementing a completely > new abstraction or simple traps for privileged operations like VMRUN. > > That way we could have a kvm that talks to xen for the VM, rendering > kvm useful on Xen dom0s, giving people the best of both worlds. > > That was only one of the ideas that came up while talking to people > why running kvm on xen isn''t as easy as just recompiling :-). Would > you think of such a thing as useful? > >Why would anyone want to do that? If you''ve got Xen running, just start up a Xen guest. -- I have a truly marvellous patch that fixes the bug which this signature is too narrow to contain. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Avi Kivity
2009-Apr-05 11:37 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support.
Alexander Graf wrote:> > On 05.04.2009, at 10:48, Avi Kivity wrote: > >> Alexander Graf wrote: >>> >>> One idea I had for full virtualization in a Xen environment would be >>> an PV vmenter/vmexit framework - either by implementing a completely >>> new abstraction or simple traps for privileged operations like VMRUN. >>> >>> That way we could have a kvm that talks to xen for the VM, rendering >>> kvm useful on Xen dom0s, giving people the best of both worlds. >>> >>> That was only one of the ideas that came up while talking to people >>> why running kvm on xen isn''t as easy as just recompiling :-). Would >>> you think of such a thing as useful? >>> >>> >> >> Why would anyone want to do that? If you''ve got Xen running, just >> start up a Xen guest. > > I''m not saying it''s a great idea - that''s why I didn''t even consider > to develop it yet :-). > > Basically it would solve two problems: > > 1) Migration path. If you could already use KVM on a Xen host, you > could have Xen PV guests and KVM guests in parallel, easing migration > to KVM for customers.I like this, of course, but we have a path through Xenner. Maybe this (kvm-on-xen) path will be easier to take.> > 2) Alternative to HVM. That''s how this came up from Gerd''s mail. We do > have KVM support in upstream qemu, but we don''t have Xen HVM support. > That way you could use the same binary for all your needs. Admittedly, > it might make more sense to just implement HVM support :-).I was under the impression that this is underway.> > Again, I just like talking to others about random ideas I have and > this was one. I don''t think it''s worth it - IMHO it''d be more useful > to create an in-kernel xen-like module that exposes Xen PV > functionality, so you get all the PV benefits without the performance > hit from full virtualization and duplication of code. >With npt/ept pv performance might be higher running under kvm+xenner than with software-only Xen by letting the guest kernel access pagetables directly. Though Gerd had some issues with 64-bit guests IIRC, which is a pity since it''s there that the pv performance hit is greatest. -- error compiling committee.c: too many arguments to function _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Avi Kivity
2009-Apr-05 12:33 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support.
Alexander Graf wrote:>>> >>> 1) Migration path. If you could already use KVM on a Xen host, you >>> could have Xen PV guests and KVM guests in parallel, easing >>> migration to KVM for customers. >> >> I like this, of course, but we have a path through Xenner. Maybe >> this (kvm-on-xen) path will be easier to take. > > Oh, I thought Xenner was semi on hold? Is it still actively developed? > I thought Gerd''s main goal for now was to get qemu-dm into upstream > qemu for now.I understood that Xenner is the next thing on the list.>> With npt/ept pv performance might be higher running under kvm+xenner >> than with software-only Xen by letting the guest kernel access >> pagetables directly. Though Gerd had some issues with 64-bit guests >> IIRC, which is a pity since it''s there that the pv performance hit is >> greatest. > > Well that might be true, but I''m not fully convinced yet :-). Either > way with "current" hardware (pre-Nehalem, pre-Barcelona) PV is still > faster.Right, but every day this is becoming less important.> Also, FWIW IO performance on PV Xen is still way superior to KVM. > Please correct me if I stand wrong there.We still have some work some work to do on virtio, but bandwidth should be comparable. And it has nothing to do with pv-vs-fv. -- error compiling committee.c: too many arguments to function _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2009-Apr-05 13:49 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 05/10] xen: add block device backend driver.
Christoph Hellwig wrote:> > > We already have bdrv_aio_readv/writev which currently linearize the > buffer underneath. Hopefully Anthony will have commited the patch to > implement the real one while I''m writing this, too :) >Unless I missed it somehow, I''m still expecting another post from you to fix the use-after-free and Win32 issues I raised on the 3/29 posting. Regards, Anthony Liguori _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-06 06:53 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support.
Alexander Graf wrote:> One idea I had for full virtualization in a Xen environment would be an > PV vmenter/vmexit framework - either by implementing a completely new > abstraction or simple traps for privileged operations like VMRUN. > > That way we could have a kvm that talks to xen for the VM, rendering kvm > useful on Xen dom0s, giving people the best of both worlds.Hmm. I suspect xen''s direct paging mode makes that quite intrusive as the kvm mmu code would have to know about this extra p2m translation step to get the shadow/ept/ntp tables right. Given that this most likely would be a odd and rarely tested use case I don''t think this would be a good idea ... cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-06 07:10 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support.
Alexander Graf wrote:> 1) Migration path. If you could already use KVM on a Xen host, you could > have Xen PV guests and KVM guests in parallel, easing migration to KVM > for customers.I still plan to make xenner handle that use case. You can also run xen inside kvm via nested svm.> 2) Alternative to HVM. That''s how this came up from Gerd''s mail. We do > have KVM support in upstream qemu, but we don''t have Xen HVM support. > That way you could use the same binary for all your needs. Admittedly, > it might make more sense to just implement HVM support :-).HVM is more difficuilt. Xen considers the tools <-> hypervisor interface private, i.e. it is a moving target. For PV this isn''t a big issue as there is little reason to actually change something. HVM usually has quite a few changes from release to release though, due to emulation changes/improvements and other reasons. cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-06 07:39 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support.
Avi Kivity wrote:> Alexander Graf wrote: >> Oh, I thought Xenner was semi on hold? Is it still actively developed? >> I thought Gerd''s main goal for now was to get qemu-dm into upstream >> qemu for now. > > I understood that Xenner is the next thing on the list.qemu-dm code is (largely) backend drivers, which are shared between xen and xenner. Thus this needs to be in first, before the xenner bits can follow. Now that this is mostly sorted I wanna un-stall the work on merging xenner. cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Christoph Hellwig
2009-Apr-06 12:38 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 05/10] xen: add block device backend driver.
On Sun, Apr 05, 2009 at 08:49:54AM -0500, Anthony Liguori wrote:> Christoph Hellwig wrote: > > > > > >We already have bdrv_aio_readv/writev which currently linearize the > >buffer underneath. Hopefully Anthony will have commited the patch to > >implement the real one while I''m writing this, too :) > > > > Unless I missed it somehow, I''m still expecting another post from you to > fix the use-after-free and Win32 issues I raised on the 3/29 posting.I assumes you wanted to fix the use after free that we found inline, but I''ll resend the series now. After your removal the patch doesn''t touch win32 code anymore and I used the mingw32 cross-compiler to verify that it still builds (it doesn''t link either with or without my patches, though) _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Stefano Stabellini
2009-Apr-06 13:56 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support.
Avi Kivity wrote:>>> 2) Alternative to HVM. That''s how this came up from Gerd''s mail. We do >> have KVM support in upstream qemu, but we don''t have Xen HVM support. >> That way you could use the same binary for all your needs. Admittedly, >> it might make more sense to just implement HVM support :-). > > I was under the impression that this is underway. >We certainly want to do it, we just haven''t got the time yet. After Gerd''s patches get accepted I think it won''t take too long to create a similar patchqueue for hvm, the two tree are getting closer and closer every month. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Jeremy Fitzhardinge
2009-Apr-06 20:44 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support.
Gerd Hoffmann wrote:> Hmm. I suspect xen''s direct paging mode makes that quite intrusive as > the kvm mmu code would have to know about this extra p2m translation > step to get the shadow/ept/ntp tables right. Given that this most > likely would be a odd and rarely tested use case I don''t think this > would be a good idea ... >That''s mostly encapsulated in the normal pagetable manipulation routines, so if kvm can be convinced to use those to manage its shadow tables, it will all Just Work. That doesn''t work well with a non-PAE 32-bit host with kvm''s PAE shadow pagetables, but Xen doesn''t support non-PAE anyway. J _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-07 10:22 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 05/10] xen: add block device backend driver.
Hi,> We already have bdrv_aio_readv/writev which currently linearize the > buffer underneath. Hopefully Anthony will have commited the patch to > implement the real one while I''m writing this, too :)Is there any specific reason to keep the nb_sectors argument for bdrv_aio_readv, other than cut&paste from the non-vectored versions? I think it is superfluous now, it can be trivially calculated using iov->size. cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Hi, Next round, addressing review comments. Bulk of the changes are just coding style, i.e. swap ordering of the funny "if (-1 == foo()) bar()" compare style. Also the FSF address is fixed everythere. And the qemu disk backend uses vectorized aio now. Short overview (individual patches have longer descriptions): #1 - groundwork for xen support (makefiles, configure, ...). #2 - backend driver core (common code used by all backends). #3 - add console backend driver. #4 - add framebuffer backend driver. With these four patches in place upstream qemu is functional aequivalent to qemu-xen for paravirtual guests. The patches are merged into qemu-xen already, with the exception of a few framebuffer bits which got hold back due to displaystate work and the most recent updates due to review comments. #5 - add block device backend driver. #6 - add net backend driver. These two patches add backend drivers for disk and network to qemu. #7 - blk & nic configuration via cmd line. #8 - pv domain builder. #9 - simplify vga selection #10 - add -vga xenfb option, configure xenfb These patches add support to qemu for creating xen pv guests. That way one can run xen guests without xend. Patch #8 is the domain builder code itself. The other patches add support for configuring devices (fb, disk, nic) via command line. The patches are also available via git: git: git://git.et.redhat.com/qemu-kraxel.git gitweb: http://git.et.redhat.com/?p=qemu-kraxel.git branches: xenbits.v5 - this patch series. qx.misc.v4 - xenfb update + review changes for qemu-xen. Ian, is just pulling from git fine with you? Or do you want the qemu-xen patches mailed to xen-devel? cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-07 14:44 UTC
[Xen-devel] [PATCH 01/10] xen: groundwork for xen support
- configure script and build system changes. - wind up new machine type. - add -xen-* command line options. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 7 +++++ configure | 34 +++++++++++++++++++++++++++ hw/boards.h | 3 ++ hw/xen.h | 20 ++++++++++++++++ hw/xen_machine_pv.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ qemu-options.hx | 11 +++++++++ target-i386/machine.c | 3 ++ vl.c | 12 +++++++++ 8 files changed, 151 insertions(+), 0 deletions(-) create mode 100644 hw/xen.h create mode 100644 hw/xen_machine_pv.c diff --git a/Makefile.target b/Makefile.target index b32d1af..1cad75e 100644 --- a/Makefile.target +++ b/Makefile.target @@ -557,6 +557,13 @@ ifdef CONFIG_BLUEZ LIBS += $(CONFIG_BLUEZ_LIBS) endif +# xen backend driver support +XEN_OBJS := xen_machine_pv.o +ifeq ($(CONFIG_XEN), yes) + OBJS += $(XEN_OBJS) + LIBS += $(XEN_LIBS) +endif + # SCSI layer OBJS+= lsi53c895a.o esp.o diff --git a/configure b/configure index 15b7856..252d90c 100755 --- a/configure +++ b/configure @@ -190,6 +190,7 @@ aix="no" blobs="yes" fdt="yes" sdl_x11="no" +xen="yes" # OS specific if check_define __linux__ ; then @@ -409,6 +410,8 @@ for opt do ;; --disable-kqemu) kqemu="no" ;; + --disable-xen) xen="no" + ;; --disable-brlapi) brlapi="no" ;; --disable-bluez) bluez="no" @@ -568,6 +571,7 @@ echo " Available drivers: $audio_possible_drivers" echo " --audio-card-list=LIST set list of emulated audio cards [$audio_card_list]" echo " Available cards: $audio_possible_cards" echo " --enable-mixemu enable mixer emulation" +echo " --disable-xen disable xen backend driver support" echo " --disable-brlapi disable BrlAPI" echo " --disable-vnc-tls disable TLS encryption for VNC server" echo " --disable-vnc-sasl disable SASL encryption for VNC server" @@ -781,6 +785,22 @@ else fi ########################################## +# xen probe + +if test "$xen" = "yes" ; then +cat > $TMPC <<EOF +#include <xenctrl.h> +#include <xs.h> +int main(void) { xs_daemon_open; xc_interface_open; } +EOF + if $cc $ARCH_CFLAGS -c -o $TMPO $TMPC -lxenstore -lxenctrl 2> /dev/null ; then + : + else + xen="no" + fi +fi + +########################################## # SDL probe sdl_too_old=no @@ -1211,6 +1231,7 @@ if test -n "$sparc_cpu"; then echo "Target Sparc Arch $sparc_cpu" fi echo "kqemu support $kqemu" +echo "xen support $xen" echo "brlapi support $brlapi" echo "Documentation $build_docs" [ ! -z "$uname_release" ] && \ @@ -1512,6 +1533,9 @@ if test "$bluez" = "yes" ; then echo "CONFIG_BLUEZ_LIBS=$bluez_libs" >> $config_mak echo "#define CONFIG_BLUEZ 1" >> $config_h fi +if test "$xen" = "yes" ; then + echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak +fi if test "$aio" = "yes" ; then echo "#define CONFIG_AIO 1" >> $config_h echo "CONFIG_AIO=yes" >> $config_mak @@ -1673,6 +1697,11 @@ case "$target_cpu" in echo "KVM_CFLAGS=$kvm_cflags" >> $config_mak echo "#define CONFIG_KVM 1" >> $config_h fi + if test "$xen" = "yes" -a "$target_softmmu" = "yes"; + then + echo "CONFIG_XEN=yes" >> $config_mak + echo "#define CONFIG_XEN 1" >> $config_h + fi ;; x86_64) echo "TARGET_ARCH=x86_64" >> $config_mak @@ -1688,6 +1717,11 @@ case "$target_cpu" in echo "KVM_CFLAGS=$kvm_cflags" >> $config_mak echo "#define CONFIG_KVM 1" >> $config_h fi + if test "$xen" = "yes" -a "$target_softmmu" = "yes" + then + echo "CONFIG_XEN=yes" >> $config_mak + echo "#define CONFIG_XEN 1" >> $config_h + fi ;; alpha) echo "TARGET_ARCH=alpha" >> $config_mak diff --git a/hw/boards.h b/hw/boards.h index 1e18ba6..215ccdc 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -34,6 +34,9 @@ extern QEMUMachine axisdev88_machine; extern QEMUMachine pc_machine; extern QEMUMachine isapc_machine; +/* xen_machine.c */ +extern QEMUMachine xenpv_machine; + /* ppc.c */ extern QEMUMachine prep_machine; extern QEMUMachine core99_machine; diff --git a/hw/xen.h b/hw/xen.h new file mode 100644 index 0000000..3c8da41 --- /dev/null +++ b/hw/xen.h @@ -0,0 +1,20 @@ +#ifndef QEMU_HW_XEN_H +#define QEMU_HW_XEN_H 1 +/* + * public xen header + * stuff needed outside xen-*.c, i.e. interfaces to qemu. + * must not depend on any xen headers being present in + * /usr/include/xen, so it can be included unconditionally. + */ + +/* xen-machine.c */ +enum xen_mode { + XEN_EMULATE = 0, // xen emulation, using xenner (default) + XEN_CREATE, // create xen domain + XEN_ATTACH // attach to xen domain created by xend +}; + +extern uint32_t xen_domid; +extern enum xen_mode xen_mode; + +#endif /* QEMU_HW_XEN_H */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c new file mode 100644 index 0000000..081df0b --- /dev/null +++ b/hw/xen_machine_pv.c @@ -0,0 +1,61 @@ +/* + * QEMU Xen PV Machine + * + * Copyright (c) 2007 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw.h" +#include "pc.h" +#include "sysemu.h" +#include "boards.h" +#include "xen.h" + +uint32_t xen_domid; +enum xen_mode xen_mode = XEN_EMULATE; + +static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + CPUState *env; + + /* Initialize a dummy CPU */ + if (cpu_model == NULL) { +#ifdef TARGET_X86_64 + cpu_model = "qemu64"; +#else + cpu_model = "qemu32"; +#endif + } + env = cpu_init(cpu_model); + env->halted = 1; +} + +QEMUMachine xenpv_machine = { + .name = "xenpv", + .desc = "Xen Para-virtualized PC", + .init = xen_init_pv, + .ram_require = (4 * 1024 * 1024) | RAMSIZE_FIXED, + .max_cpus = 1, +}; diff --git a/qemu-options.hx b/qemu-options.hx index c40ea1e..9fc0625 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1305,6 +1305,17 @@ Enable KVM full virtualization support. This option is only available if KVM support is enabled when compiling. ETEXI +#ifdef CONFIG_XEN +DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid, + "-xen-domid id specify xen guest domain id\n") +DEF("xen-create", 0, QEMU_OPTION_xen_create, + "-xen-create create domain using xen hypercalls, bypassing xend\n" + " warning: should not be used when xend is in use\n") +DEF("xen-attach", 0, QEMU_OPTION_xen_attach, + "-xen-attach attach to existing xen domain\n" + " xend will use this when starting qemu\n") +#endif + DEF("no-reboot", 0, QEMU_OPTION_no_reboot, \ "-no-reboot exit instead of rebooting\n") STEXI diff --git a/target-i386/machine.c b/target-i386/machine.c index 1cf49d5..1b0d36d 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -9,6 +9,9 @@ void register_machines(void) { qemu_register_machine(&pc_machine); qemu_register_machine(&isapc_machine); +#ifdef CONFIG_XEN + qemu_register_machine(&xenpv_machine); +#endif } static void cpu_put_seg(QEMUFile *f, SegmentCache *dt) diff --git a/vl.c b/vl.c index 4bd173f..889aaf6 100644 --- a/vl.c +++ b/vl.c @@ -138,6 +138,7 @@ int main(int argc, char **argv) #include "hw/isa.h" #include "hw/baum.h" #include "hw/bt.h" +#include "hw/xen.h" #include "bt-host.h" #include "net.h" #include "monitor.h" @@ -4949,6 +4950,17 @@ int main(int argc, char **argv, char **envp) run_as = optarg; break; #endif +#ifdef CONFIG_XEN + case QEMU_OPTION_xen_domid: + xen_domid = atoi(optarg); + break; + case QEMU_OPTION_xen_create: + xen_mode = XEN_CREATE; + break; + case QEMU_OPTION_xen_attach: + xen_mode = XEN_ATTACH; + break; +#endif } } } -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
This patch adds infrastructure for xen backend drivers living in qemu, so drivers don''t need to implement common stuff on their own. It''s mostly xenbus management stuff: some functions to access xentore, setting up xenstore watches, callbacks on device discovery and state changes, handle event channel, ... Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen_backend.c | 691 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_backend.h | 86 +++++++ hw/xen_common.h | 34 +++ hw/xen_machine_pv.c | 8 +- 5 files changed, 819 insertions(+), 2 deletions(-) create mode 100644 hw/xen_backend.c create mode 100644 hw/xen_backend.h create mode 100644 hw/xen_common.h diff --git a/Makefile.target b/Makefile.target index 1cad75e..5d9dfc7 100644 --- a/Makefile.target +++ b/Makefile.target @@ -558,7 +558,7 @@ LIBS += $(CONFIG_BLUEZ_LIBS) endif # xen backend driver support -XEN_OBJS := xen_machine_pv.o +XEN_OBJS := xen_machine_pv.o xen_backend.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.c b/hw/xen_backend.c new file mode 100644 index 0000000..b13c3f9 --- /dev/null +++ b/hw/xen_backend.c @@ -0,0 +1,691 @@ +/* + * xen backend driver infrastructure + * (c) 2008 Gerd Hoffmann <kraxel@redhat.com> + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * TODO: add some xenbus / xenstore concepts overview here. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/signal.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/grant_table.h> + +#include "hw.h" +#include "qemu-char.h" +#include "xen_backend.h" + +/* ------------------------------------------------------------- */ + +/* public */ +int xen_xc; +struct xs_handle *xenstore = NULL; + +/* private */ +static TAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = TAILQ_HEAD_INITIALIZER(xendevs); +static int debug = 0; + +/* ------------------------------------------------------------- */ + +int xenstore_write_str(const char *base, const char *node, const char *val) +{ + char abspath[XEN_BUFSIZE]; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + if (!xs_write(xenstore, 0, abspath, val, strlen(val))) + return -1; + return 0; +} + +char *xenstore_read_str(const char *base, const char *node) +{ + char abspath[XEN_BUFSIZE]; + unsigned int len; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + return xs_read(xenstore, 0, abspath, &len); +} + +int xenstore_write_int(const char *base, const char *node, int ival) +{ + char val[32]; + + snprintf(val, sizeof(val), "%d", ival); + return xenstore_write_str(base, node, val); +} + +int xenstore_read_int(const char *base, const char *node, int *ival) +{ + char *val; + int rc = -1; + + val = xenstore_read_str(base, node); + if (val && 1 == sscanf(val, "%d", ival)) + rc = 0; + qemu_free(val); + return rc; +} + +int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val) +{ + return xenstore_write_str(xendev->be, node, val); +} + +int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival) +{ + return xenstore_write_int(xendev->be, node, ival); +} + +char *xenstore_read_be_str(struct XenDevice *xendev, const char *node) +{ + return xenstore_read_str(xendev->be, node); +} + +int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival) +{ + return xenstore_read_int(xendev->be, node, ival); +} + +char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node) +{ + return xenstore_read_str(xendev->fe, node); +} + +int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival) +{ + return xenstore_read_int(xendev->fe, node, ival); +} + +/* ------------------------------------------------------------- */ + +const char *xenbus_strstate(enum xenbus_state state) +{ + static const char *const name[] = { + [ XenbusStateUnknown ] = "Unknown", + [ XenbusStateInitialising ] = "Initialising", + [ XenbusStateInitWait ] = "InitWait", + [ XenbusStateInitialised ] = "Initialised", + [ XenbusStateConnected ] = "Connected", + [ XenbusStateClosing ] = "Closing", + [ XenbusStateClosed ] = "Closed", + }; + return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; +} + +int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) +{ + int rc; + + rc = xenstore_write_be_int(xendev, "state", state); + if (rc < 0) + return rc; + xen_be_printf(xendev, 1, "backend state: %s -> %s\n", + xenbus_strstate(xendev->be_state), xenbus_strstate(state)); + xendev->be_state = state; + return 0; +} + +/* ------------------------------------------------------------- */ + +struct XenDevice *xen_be_find_xendev(const char *type, int dom, int dev) +{ + struct XenDevice *xendev; + + TAILQ_FOREACH(xendev, &xendevs, next) { + if (xendev->dom != dom) + continue; + if (xendev->dev != dev) + continue; + if (0 != strcmp(xendev->type, type)) + continue; + return xendev; + } + return NULL; +} + +/* + * get xen backend device, allocate a new one if it doesn''t exist. + */ +static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, + struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char *dom0; + + xendev = xen_be_find_xendev(type, dom, dev); + if (xendev) + return xendev; + + /* init new xendev */ + xendev = qemu_mallocz(ops->size); + if (!xendev) + return NULL; + xendev->type = type; + xendev->dom = dom; + xendev->dev = dev; + xendev->ops = ops; + + dom0 = xs_get_domain_path(xenstore, 0); + snprintf(xendev->be, sizeof(xendev->be), "%s/backend/%s/%d/%d", + dom0, xendev->type, xendev->dom, xendev->dev); + snprintf(xendev->name, sizeof(xendev->name), "%s-%d", + xendev->type, xendev->dev); + free(dom0); + + xendev->debug = debug; + xendev->local_port = -1; + + xendev->evtchndev = xc_evtchn_open(); + if (xendev->evtchndev < 0) { + fprintf(stderr, "can''t open evtchn device\n"); + qemu_free(xendev); + return NULL; + } + fcntl(xc_evtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC); + + if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { + xendev->gnttabdev = xc_gnttab_open(); + if (xendev->gnttabdev < 0) { + fprintf(stderr, "can''t open gnttab device\n"); + xc_evtchn_close(xendev->evtchndev); + qemu_free(xendev); + return NULL; + } + } else { + xendev->gnttabdev = -1; + } + + TAILQ_INSERT_TAIL(&xendevs, xendev, next); + + if (xendev->ops->alloc) + xendev->ops->alloc(xendev); + + return xendev; +} + +/* + * release xen backend device. + */ +static struct XenDevice *xen_be_del_xendev(int dom, int dev) +{ + struct XenDevice *xendev, *xnext; + + /* + * This is pretty much like TAILQ_FOREACH(xendev, &xendevs, next) but + * we save the next pointer in xnext because we might free xendev. + */ + xnext = xendevs.tqh_first; + while (xnext) { + xendev = xnext; + xnext = xendev->next.tqe_next; + + if (xendev->dom != dom) + continue; + if (xendev->dev != dev && dev != -1) + continue; + + if (xendev->ops->free) + xendev->ops->free(xendev); + + if (xendev->fe) { + char token[XEN_BUFSIZE]; + snprintf(token, sizeof(token), "fe:%p", xendev); + xs_unwatch(xenstore, xendev->fe, token); + qemu_free(xendev->fe); + } + + if (xendev->evtchndev >= 0) + xc_evtchn_close(xendev->evtchndev); + if (xendev->gnttabdev >= 0) + xc_gnttab_close(xendev->gnttabdev); + + TAILQ_REMOVE(&xendevs, xendev, next); + qemu_free(xendev); + } + return NULL; +} + +/* + * Sync internal data structures on xenstore updates. + * Node specifies the changed field. node = NULL means + * update all fields (used for initialization). + */ +static void xen_be_backend_changed(struct XenDevice *xendev, const char *node) +{ + if (node == NULL || strcmp(node, "online") == 0) { + if (xenstore_read_be_int(xendev, "online", &xendev->online) == -1) + xendev->online = 0; + } + + if (node) { + xen_be_printf(xendev, 2, "backend update: %s\n", node); + if (xendev->ops->backend_changed) + xendev->ops->backend_changed(xendev, node); + } +} + +static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node) +{ + int fe_state; + + if (node == NULL || strcmp(node, "state") == 0) { + if (xenstore_read_fe_int(xendev, "state", &fe_state) == -1) + fe_state = XenbusStateUnknown; + if (xendev->fe_state != fe_state) + xen_be_printf(xendev, 1, "frontend state: %s -> %s\n", + xenbus_strstate(xendev->fe_state), + xenbus_strstate(fe_state)); + xendev->fe_state = fe_state; + } + if (node == NULL || strcmp(node, "protocol") == 0) { + qemu_free(xendev->protocol); + xendev->protocol = xenstore_read_fe_str(xendev, "protocol"); + if (xendev->protocol) + xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol); + } + + if (node) { + xen_be_printf(xendev, 2, "frontend update: %s\n", node); + if (xendev->ops->frontend_changed) + xendev->ops->frontend_changed(xendev, node); + } +} + +/* ------------------------------------------------------------- */ +/* Check for possible state transitions and perform them. */ + +/* + * Initial xendev setup. Read frontend path, register watch for it. + * Should succeed once xend finished setting up the backend device. + * + * Also sets initial state (-> Initializing) when done. Which + * only affects the xendev->be_state variable as xenbus should + * already be put into that state by xend. + */ +static int xen_be_try_setup(struct XenDevice *xendev) +{ + char token[XEN_BUFSIZE]; + int be_state; + + if (xenstore_read_be_int(xendev, "state", &be_state) == -1) { + xen_be_printf(xendev, 0, "reading backend state failed\n"); + return -1; + } + + if (be_state != XenbusStateInitialising) { + xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n", + xenbus_strstate(be_state)); + return -1; + } + + xendev->fe = xenstore_read_be_str(xendev, "frontend"); + if (xendev->fe == NULL) { + xen_be_printf(xendev, 0, "reading frontend path failed\n"); + return -1; + } + + /* setup frontend watch */ + snprintf(token, sizeof(token), "fe:%p", xendev); + if (!xs_watch(xenstore, xendev->fe, token)) { + xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n", + xendev->fe); + return -1; + } + xen_be_set_state(xendev, XenbusStateInitialising); + + xen_be_backend_changed(xendev, NULL); + xen_be_frontend_changed(xendev, NULL); + return 0; +} + +/* + * Try initialize xendev. Prepare everything the backend can do + * without synchronizing with the frontend. Fakes hotplug-status. No + * hotplug involved here because this is about userspace drivers, thus + * there are kernel backend devices which could invoke hotplug. + * + * Goes to InitWait on success. + */ +static int xen_be_try_init(struct XenDevice *xendev) +{ + int rc = 0; + + if (!xendev->online) { + xen_be_printf(xendev, 1, "not online\n"); + return -1; + } + + if (xendev->ops->init) + rc = xendev->ops->init(xendev); + if (0 != rc) { + xen_be_printf(xendev, 1, "init() failed\n"); + return rc; + } + + xenstore_write_be_str(xendev, "hotplug-status", "connected"); + xen_be_set_state(xendev, XenbusStateInitWait); + return 0; +} + +/* + * Try to connect xendev. Depends on the frontend being ready + * for it (shared ring and evtchn info in xenstore, state being + * Initialised or Connected). + * + * Goes to Connected on success. + */ +static int xen_be_try_connect(struct XenDevice *xendev) +{ + int rc = 0; + + if (xendev->fe_state != XenbusStateInitialised && + xendev->fe_state != XenbusStateConnected) { + if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { + xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); + } else { + xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); + return -1; + } + } + + if (xendev->ops->connect) + rc = xendev->ops->connect(xendev); + if (0 != rc) { + xen_be_printf(xendev, 0, "connect() failed\n"); + return rc; + } + + xen_be_set_state(xendev, XenbusStateConnected); + return 0; +} + +/* + * Teardown connection. + * + * Goes to Closed when done. + */ +static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state) +{ + if (xendev->be_state != XenbusStateClosing && + xendev->be_state != XenbusStateClosed && + xendev->ops->disconnect) + xendev->ops->disconnect(xendev); + if (xendev->be_state != state) + xen_be_set_state(xendev, state); +} + +/* + * Try to reset xendev, for reconnection by another frontend instance. + */ +static int xen_be_try_reset(struct XenDevice *xendev) +{ + if (xendev->fe_state != XenbusStateInitialising) + return -1; + + xen_be_printf(xendev, 1, "device reset (for re-connect)\n"); + xen_be_set_state(xendev, XenbusStateInitialising); + return 0; +} + +/* + * state change dispatcher function + */ +void xen_be_check_state(struct XenDevice *xendev) +{ + int rc = 0; + + /* frontend may request shutdown from almost anywhere */ + if (xendev->fe_state == XenbusStateClosing || + xendev->fe_state == XenbusStateClosed) { + xen_be_disconnect(xendev, xendev->fe_state); + return; + } + + /* check for possible backend state transitions */ + for (;;) { + switch (xendev->be_state) { + case XenbusStateUnknown: + rc = xen_be_try_setup(xendev); + break; + case XenbusStateInitialising: + rc = xen_be_try_init(xendev); + break; + case XenbusStateInitWait: + rc = xen_be_try_connect(xendev); + break; + case XenbusStateClosed: + rc = xen_be_try_reset(xendev); + break; + default: + rc = -1; + } + if (0 != rc) + break; + } +} + +/* ------------------------------------------------------------- */ + +static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char path[XEN_BUFSIZE], token[XEN_BUFSIZE]; + char **dev = NULL, *dom0; + unsigned int cdev, j; + + /* setup watch */ + dom0 = xs_get_domain_path(xenstore, 0); + snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops); + snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom); + free(dom0); + if (!xs_watch(xenstore, path, token)) { + fprintf(stderr, "xen be: watching backend path (%s) failed\n", path); + return -1; + } + + /* look for backends */ + dev = xs_directory(xenstore, 0, path, &cdev); + if (!dev) + return 0; + for (j = 0; j < cdev; j++) { + xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops); + if (xendev == NULL) + continue; + xen_be_check_state(xendev); + } + qemu_free(dev); + return 0; +} + +static void xenstore_update_be(char *watch, char *type, int dom, + struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char path[XEN_BUFSIZE], *dom0; + unsigned int len, dev; + + dom0 = xs_get_domain_path(xenstore, 0); + len = snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom); + free(dom0); + if (0 != strncmp(path, watch, len)) + return; + if (2 != sscanf(watch+len, "/%u/%255s", &dev, path)) { + strcpy(path, ""); + if (1 != sscanf(watch+len, "/%u", &dev)) + dev = -1; + } + if (dev == -1) + return; + + if (0) { + /* FIXME: detect devices being deleted from xenstore ... */ + xen_be_del_xendev(dom, dev); + } + + xendev = xen_be_get_xendev(type, dom, dev, ops); + if (NULL != xendev) { + xen_be_backend_changed(xendev, path); + xen_be_check_state(xendev); + } +} + +static void xenstore_update_fe(char *watch, struct XenDevice *xendev) +{ + char *node; + unsigned int len; + + len = strlen(xendev->fe); + if (0 != strncmp(xendev->fe, watch, len)) + return; + if (watch[len] != ''/'') + return; + node = watch + len + 1; + + xen_be_frontend_changed(xendev, node); + xen_be_check_state(xendev); +} + +static void xenstore_update(void *unused) +{ + char **vec = NULL; + intptr_t type, ops, ptr; + unsigned int dom, count; + + vec = xs_read_watch(xenstore, &count); + if (vec == NULL) + goto cleanup; + + if (3 == sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR, + &type, &dom, &ops)) + xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops); + if (1 == sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr)) + xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr); + +cleanup: + qemu_free(vec); +} + +static void xen_be_evtchn_event(void *opaque) +{ + struct XenDevice *xendev = opaque; + evtchn_port_t port; + + port = xc_evtchn_pending(xendev->evtchndev); + if (port != xendev->local_port) { + xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n", + port, xendev->local_port); + return; + } + xc_evtchn_unmask(xendev->evtchndev, port); + + if (xendev->ops->event) + xendev->ops->event(xendev); +} + +/* -------------------------------------------------------------------- */ + +int xen_be_init(void) +{ + xenstore = xs_daemon_open(); + if (!xenstore) { + fprintf(stderr, "can''t connect to xenstored\n"); + return -1; + } + + if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) + goto err; + + xen_xc = xc_interface_open(); + if (xen_xc == -1) { + fprintf(stderr, "can''t open xen interface\n"); + goto err; + } + return 0; + +err: + qemu_set_fd_handler(xs_fileno(xenstore), NULL, NULL, NULL); + xs_daemon_close(xenstore); + xenstore = NULL; + + return -1; +} + +int xen_be_register(const char *type, struct XenDevOps *ops) +{ + return xenstore_scan(type, xen_domid, ops); +} + +int xen_be_bind_evtchn(struct XenDevice *xendev) +{ + if (xendev->local_port != -1) + return 0; + xendev->local_port = xc_evtchn_bind_interdomain + (xendev->evtchndev, xendev->dom, xendev->remote_port); + if (xendev->local_port == -1) { + xen_be_printf(xendev, 0, "xc_evtchn_bind_interdomain failed\n"); + return -1; + } + xen_be_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), + xen_be_evtchn_event, NULL, xendev); + return 0; +} + +void xen_be_unbind_evtchn(struct XenDevice *xendev) +{ + if (xendev->local_port == -1) + return; + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL); + xc_evtchn_unbind(xendev->evtchndev, xendev->local_port); + xen_be_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port); + xendev->local_port = -1; +} + +int xen_be_send_notify(struct XenDevice *xendev) +{ + return xc_evtchn_notify(xendev->evtchndev, xendev->local_port); +} + +/* + * msg_level: + * 0 == errors. + * 1 == informative debug messages. + * 2 == noisy debug messages. + * 3 == will flood your log. + */ +void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) +{ + va_list args; + + if (msg_level > xendev->debug) + return; + fprintf(stderr, "xen be: %s: ", xendev->name); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} diff --git a/hw/xen_backend.h b/hw/xen_backend.h new file mode 100644 index 0000000..a1243f6 --- /dev/null +++ b/hw/xen_backend.h @@ -0,0 +1,86 @@ +#ifndef QEMU_HW_XEN_BACKEND_H +#define QEMU_HW_XEN_BACKEND_H 1 + +#include "xen_common.h" + +/* ------------------------------------------------------------- */ + +#define XEN_BUFSIZE 1024 + +struct XenDevice; + +/* driver uses grant tables -> open gntdev device (xendev->gnttabdev) */ +#define DEVOPS_FLAG_NEED_GNTDEV 1 +/* don''t expect frontend doing correct state transitions (aka console quirk) */ +#define DEVOPS_FLAG_IGNORE_STATE 2 + +struct XenDevOps { + size_t size; + uint32_t flags; + void (*alloc)(struct XenDevice *xendev); + int (*init)(struct XenDevice *xendev); + int (*connect)(struct XenDevice *xendev); + void (*event)(struct XenDevice *xendev); + void (*disconnect)(struct XenDevice *xendev); + int (*free)(struct XenDevice *xendev); + void (*backend_changed)(struct XenDevice *xendev, const char *node); + void (*frontend_changed)(struct XenDevice *xendev, const char *node); +}; + +struct XenDevice { + const char *type; + int dom; + int dev; + char name[64]; + int debug; + + enum xenbus_state be_state; + enum xenbus_state fe_state; + int online; + char be[XEN_BUFSIZE]; + char *fe; + char *protocol; + int remote_port; + int local_port; + + int evtchndev; + int gnttabdev; + + struct XenDevOps *ops; + TAILQ_ENTRY(XenDevice) next; +}; + +/* ------------------------------------------------------------- */ + +/* variables */ +extern int xen_xc; +extern struct xs_handle *xenstore; + +/* xenstore helper functions */ +int xenstore_write_str(const char *base, const char *node, const char *val); +int xenstore_write_int(const char *base, const char *node, int ival); +char *xenstore_read_str(const char *base, const char *node); +int xenstore_read_int(const char *base, const char *node, int *ival); + +int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val); +int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival); +char *xenstore_read_be_str(struct XenDevice *xendev, const char *node); +int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival); +char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node); +int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival); + +const char *xenbus_strstate(enum xenbus_state state); +struct XenDevice *xen_be_find_xendev(const char *type, int dom, int dev); +void xen_be_check_state(struct XenDevice *xendev); + +/* xen backend driver bits */ +int xen_be_init(void); +int xen_be_register(const char *type, struct XenDevOps *ops); +int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state); +int xen_be_bind_evtchn(struct XenDevice *xendev); +void xen_be_unbind_evtchn(struct XenDevice *xendev); +int xen_be_send_notify(struct XenDevice *xendev); +void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) + __attribute__ ((format(printf, 3, 4))); + +#endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_common.h b/hw/xen_common.h new file mode 100644 index 0000000..7562567 --- /dev/null +++ b/hw/xen_common.h @@ -0,0 +1,34 @@ +#ifndef QEMU_HW_XEN_COMMON_H +#define QEMU_HW_XEN_COMMON_H 1 + +#include <stddef.h> +#include <inttypes.h> + +#include <xenctrl.h> +#include <xs.h> +#include <xen/io/xenbus.h> + +#include "hw.h" +#include "xen.h" +#include "sys-queue.h" /* BSD list implementation */ + +/* + * tweaks needed to build with different xen versions + * 0x00030205 -> 3.1.0 + * 0x00030207 -> 3.2.0 + * 0x00030208 -> unstable + */ +#include <xen/xen-compat.h> +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030205 +# define evtchn_port_or_error_t int +#endif +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030207 +# define xc_map_foreign_pages xc_map_foreign_batch +#endif +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030208 +# define xen_mb() mb() +# define xen_rmb() rmb() +# define xen_wmb() wmb() +#endif + +#endif /* QEMU_HW_XEN_COMMON_H */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 081df0b..6b2f8f6 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -26,7 +26,7 @@ #include "pc.h" #include "sysemu.h" #include "boards.h" -#include "xen.h" +#include "xen_backend.h" uint32_t xen_domid; enum xen_mode xen_mode = XEN_EMULATE; @@ -50,6 +50,12 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, } env = cpu_init(cpu_model); env->halted = 1; + + /* Initialize backend core & drivers */ + if (-1 == xen_be_init()) { + fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); + exit(1); + } } QEMUMachine xenpv_machine = { -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-07 14:44 UTC
[Xen-devel] [PATCH 03/10] xen: add console backend driver.
This patch adds a xenconsole backend driver. It it based on current xen-unstable code. It has been changed to make use of the common backend driver code. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 1 + hw/xen_backend.h | 3 + hw/xen_console.c | 276 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_machine_pv.c | 1 + 4 files changed, 281 insertions(+), 0 deletions(-) create mode 100644 hw/xen_console.c diff --git a/Makefile.target b/Makefile.target index 5d9dfc7..a3df83b 100644 --- a/Makefile.target +++ b/Makefile.target @@ -559,6 +559,7 @@ endif # xen backend driver support XEN_OBJS := xen_machine_pv.o xen_backend.o +XEN_OBJS += xen_console.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.h b/hw/xen_backend.h index a1243f6..4744713 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -83,4 +83,7 @@ int xen_be_send_notify(struct XenDevice *xendev); void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) __attribute__ ((format(printf, 3, 4))); +/* actual backend drivers */ +extern struct XenDevOps xen_console_ops; /* xen_console.c */ + #endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_console.c b/hw/xen_console.c new file mode 100644 index 0000000..707075e --- /dev/null +++ b/hw/xen_console.c @@ -0,0 +1,276 @@ +/* + * Copyright (C) International Business Machines Corp., 2005 + * Author(s): Anthony Liguori <aliguori@us.ibm.com> + * + * Copyright (C) Red Hat 2007 + * + * Xen Console + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/select.h> +#include <fcntl.h> +#include <unistd.h> +#include <termios.h> +#include <stdarg.h> +#include <sys/mman.h> +#include <xs.h> +#include <xen/io/console.h> +#include <xenctrl.h> + +#include "hw.h" +#include "sysemu.h" +#include "qemu-char.h" +#include "xen_backend.h" + +#define dolog(val, fmt, ...) fprintf(stderr, fmt "\n", ## __VA_ARGS__) + +struct buffer { + uint8_t *data; + size_t consumed; + size_t size; + size_t capacity; + size_t max_capacity; +}; + +struct XenConsole { + struct XenDevice xendev; /* must be first */ + struct buffer buffer; + char console[XEN_BUFSIZE]; + int ring_ref; + void *sring; + CharDriverState *chr; + int backlog; +}; + +static void buffer_append(struct XenConsole *con) +{ + struct buffer *buffer = &con->buffer; + XENCONS_RING_IDX cons, prod, size; + struct xencons_interface *intf = con->sring; + + cons = intf->out_cons; + prod = intf->out_prod; + xen_mb(); + + size = prod - cons; + if ((size == 0) || (size > sizeof(intf->out))) + return; + + if ((buffer->capacity - buffer->size) < size) { + buffer->capacity += (size + 1024); + buffer->data = qemu_realloc(buffer->data, buffer->capacity); + if (buffer->data == NULL) { + dolog(LOG_ERR, "Memory allocation failed"); + exit(ENOMEM); + } + } + + while (cons != prod) + buffer->data[buffer->size++] = intf->out[ + MASK_XENCONS_IDX(cons++, intf->out)]; + + xen_mb(); + intf->out_cons = cons; + xen_be_send_notify(&con->xendev); + + if (buffer->max_capacity && + buffer->size > buffer->max_capacity) { + /* Discard the middle of the data. */ + + size_t over = buffer->size - buffer->max_capacity; + uint8_t *maxpos = buffer->data + buffer->max_capacity; + + memmove(maxpos - over, maxpos, over); + buffer->data = qemu_realloc(buffer->data, buffer->max_capacity); + buffer->size = buffer->capacity = buffer->max_capacity; + + if (buffer->consumed > buffer->max_capacity - over) + buffer->consumed = buffer->max_capacity - over; + } +} + +static void buffer_advance(struct buffer *buffer, size_t len) +{ + buffer->consumed += len; + if (buffer->consumed == buffer->size) { + buffer->consumed = 0; + buffer->size = 0; + } +} + +static int ring_free_bytes(struct XenConsole *con) +{ + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX cons, prod, space; + + cons = intf->in_cons; + prod = intf->in_prod; + xen_mb(); + + space = prod - cons; + if (space > sizeof(intf->in)) + return 0; /* ring is screwed: ignore it */ + + return (sizeof(intf->in) - space); +} + +static int xencons_can_receive(void *opaque) +{ + struct XenConsole *con = opaque; + return ring_free_bytes(con); +} + +static void xencons_receive(void *opaque, const uint8_t *buf, int len) +{ + struct XenConsole *con = opaque; + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX prod; + int i, max; + + max = ring_free_bytes(con); + /* The can_receive() func limits this, but check again anyway */ + if (max < len) + len = max; + + prod = intf->in_prod; + for (i = 0; i < len; i++) { + intf->in[MASK_XENCONS_IDX(prod++, intf->in)] + buf[i]; + } + xen_wmb(); + intf->in_prod = prod; + xen_be_send_notify(&con->xendev); +} + +static void xencons_send(struct XenConsole *con) +{ + ssize_t len, size; + + size = con->buffer.size - con->buffer.consumed; + if (con->chr) + len = qemu_chr_write(con->chr, con->buffer.data + con->buffer.consumed, + size); + else + len = size; + if (len < 1) { + if (!con->backlog) { + con->backlog = 1; + xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n"); + } + } else { + buffer_advance(&con->buffer, len); + if (con->backlog && len == size) { + con->backlog = 0; + xen_be_printf(&con->xendev, 1, "backlog is gone\n"); + } + } +} + +/* -------------------------------------------------------------------- */ + +static int con_init(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + char *type, *dom; + + /* setup */ + dom = xs_get_domain_path(xenstore, con->xendev.dom); + snprintf(con->console, sizeof(con->console), "%s/console", dom); + free(dom); + + type = xenstore_read_str(con->console, "type"); + if (!type || 0 != strcmp(type, "ioemu")) { + xen_be_printf(xendev, 1, "not for me (type=%s)\n", type); + return -1; + } + + if (!serial_hds[con->xendev.dev]) + xen_be_printf(xendev, 1, "WARNING: serial line %d not configured\n", + con->xendev.dev); + else + con->chr = serial_hds[con->xendev.dev]; + + return 0; +} + +static int con_connect(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + int limit; + + if (xenstore_read_int(con->console, "ring-ref", &con->ring_ref) == -1) + return -1; + if (xenstore_read_int(con->console, "port", &con->xendev.remote_port) == -1) + return -1; + if (xenstore_read_int(con->console, "limit", &limit) == 0) + con->buffer.max_capacity = limit; + + con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom, + XC_PAGE_SIZE, + PROT_READ|PROT_WRITE, + con->ring_ref); + if (!con->sring) + return -1; + + xen_be_bind_evtchn(&con->xendev); + if (con->chr) + qemu_chr_add_handlers(con->chr, xencons_can_receive, xencons_receive, + NULL, con); + + xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", + con->ring_ref, + con->xendev.remote_port, + con->xendev.local_port, + con->buffer.max_capacity); + return 0; +} + +static void con_disconnect(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + + if (con->chr) + qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); + xen_be_unbind_evtchn(&con->xendev); + + if (con->sring) { + munmap(con->sring, XC_PAGE_SIZE); + con->sring = NULL; + } +} + +static void con_event(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + + buffer_append(con); + if (con->buffer.size - con->buffer.consumed) + xencons_send(con); +} + +/* -------------------------------------------------------------------- */ + +struct XenDevOps xen_console_ops = { + .size = sizeof(struct XenConsole), + .flags = DEVOPS_FLAG_IGNORE_STATE, + .init = con_init, + .connect = con_connect, + .event = con_event, + .disconnect = con_disconnect, +}; diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 6b2f8f6..925280d 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -56,6 +56,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); exit(1); } + xen_be_register("console", &xen_console_ops); } QEMUMachine xenpv_machine = { -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-07 14:44 UTC
[Xen-devel] [PATCH 04/10] xen: add framebuffer backend driver
This patch adds a frsamebuffer (and kbd+mouse) backend driver. It it based on current xen-unstable code. It has been changed to make use of the common backend driver code. It also has been changed to compile with xen headers older than release 3.3 Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen_backend.h | 4 + hw/xen_machine_pv.c | 5 + hw/xenfb.c | 1017 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1027 insertions(+), 1 deletions(-) create mode 100644 hw/xenfb.c diff --git a/Makefile.target b/Makefile.target index a3df83b..ac082d2 100644 --- a/Makefile.target +++ b/Makefile.target @@ -559,7 +559,7 @@ endif # xen backend driver support XEN_OBJS := xen_machine_pv.o xen_backend.o -XEN_OBJS += xen_console.o +XEN_OBJS += xen_console.o xenfb.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.h b/hw/xen_backend.h index 4744713..e9a4e2d 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -85,5 +85,9 @@ void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ... /* actual backend drivers */ extern struct XenDevOps xen_console_ops; /* xen_console.c */ +extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ +extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ + +void xen_init_display(int domid); #endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 925280d..24ec920 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -57,6 +57,11 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, exit(1); } xen_be_register("console", &xen_console_ops); + xen_be_register("vkbd", &xen_kbdmouse_ops); + xen_be_register("vfb", &xen_framebuffer_ops); + + /* setup framebuffer */ + xen_init_display(xen_domid); } QEMUMachine xenpv_machine = { diff --git a/hw/xenfb.c b/hw/xenfb.c new file mode 100644 index 0000000..2783103 --- /dev/null +++ b/hw/xenfb.c @@ -0,0 +1,1017 @@ +/* + * xen paravirt framebuffer backend + * + * Copyright IBM, Corp. 2005-2006 + * Copyright Red Hat, Inc. 2006-2008 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com>, + * Markus Armbruster <armbru@redhat.com>, + * Daniel P. Berrange <berrange@redhat.com>, + * Pat Campbell <plc@novell.com>, + * Gerd Hoffmann <kraxel@redhat.com> + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdbool.h> +#include <sys/mman.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/event_channel.h> +#include <xen/io/xenbus.h> +#include <xen/io/fbif.h> +#include <xen/io/kbdif.h> +#include <xen/io/protocols.h> + +#include "hw.h" +#include "sysemu.h" +#include "console.h" +#include "qemu-char.h" +#include "xen_backend.h" + +#ifndef BTN_LEFT +#define BTN_LEFT 0x110 /* from <linux/input.h> */ +#endif + +/* -------------------------------------------------------------------- */ + +struct common { + struct XenDevice xendev; /* must be first */ + void *page; + DisplayState *ds; +}; + +struct XenInput { + struct common c; + int abs_pointer_wanted; /* Whether guest supports absolute pointer */ + int button_state; /* Last seen pointer button state */ + int extended; + QEMUPutMouseEntry *qmouse; +}; + +#define UP_QUEUE 8 + +struct XenFB { + struct common c; + size_t fb_len; + int row_stride; + int depth; + int width; + int height; + int offset; + void *pixels; + int fbpages; + int feature_update; + int refresh_period; + int bug_trigger; + int have_console; + int do_resize; + + struct { + int x,y,w,h; + } up_rects[UP_QUEUE]; + int up_count; + int up_fullscreen; +}; + +/* -------------------------------------------------------------------- */ + +static int common_bind(struct common *c) +{ + int mfn; + + if (xenstore_read_fe_int(&c->xendev, "page-ref", &mfn) == -1) + return -1; + if (xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port) == -1) + return -1; + + c->page = xc_map_foreign_range(xen_xc, c->xendev.dom, + XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, mfn); + if (c->page == NULL) + return -1; + + xen_be_bind_evtchn(&c->xendev); + xen_be_printf(&c->xendev, 1, "ring mfn %d, remote-port %d, local-port %d\n", + mfn, c->xendev.remote_port, c->xendev.local_port); + + return 0; +} + +static void common_unbind(struct common *c) +{ + xen_be_unbind_evtchn(&c->xendev); + if (c->page) { + munmap(c->page, XC_PAGE_SIZE); + c->page = NULL; + } +} + +/* -------------------------------------------------------------------- */ + +#if 0 +/* + * These two tables are not needed any more, but left in here + * intentionally as documentation, to show how scancode2linux[] + * was generated. + * + * Tables to map from scancode to Linux input layer keycode. + * Scancodes are hardware-specific. These maps assumes a + * standard AT or PS/2 keyboard which is what QEMU feeds us. + */ +const unsigned char atkbd_set2_keycode[512] = { + + 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, + 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, + 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, + 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, + 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, + 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85, + 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0, + 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, + 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, + 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142, + 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, + 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, + +}; + +const unsigned char atkbd_unxlate_table[128] = { + + 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, + 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, + 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, + 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, + 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 + +}; +#endif + +/* + * for (i = 0; i < 128; i++) { + * scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; + * scancode2linux[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; + * } + */ +static const unsigned char scancode2linux[512] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 99, 0, 86, 87, 88,117, 0, 0, 95,183,184,185, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 93, 0, 0, 89, 0, 0, 85, 91, 90, 92, 0, 94, 0,124,121, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 165, 0, 0, 0, 0, 0, 0, 0, 0,163, 0, 0, 96, 97, 0, 0, + 113,140,164, 0,166, 0, 0, 0, 0, 0,255, 0, 0, 0,114, 0, + 115, 0,150, 0, 0, 98,255, 99,100, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,119,119,102,103,104, 0,105,112,106,118,107, + 108,109,110,111, 0, 0, 0, 0, 0, 0, 0,125,126,127,116,142, + 0, 0, 0,143, 0,217,156,173,128,159,158,157,155,226, 0,112, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Send an event to the keyboard frontend driver */ +static int xenfb_kbd_event(struct XenInput *xenfb, + union xenkbd_in_event *event) +{ + struct xenkbd_page *page = xenfb->c.page; + uint32_t prod; + + if (xenfb->c.xendev.be_state != XenbusStateConnected) + return 0; + if (!page) + return 0; + + prod = page->in_prod; + if (prod - page->in_cons == XENKBD_IN_RING_LEN) { + errno = EAGAIN; + return -1; + } + + xen_mb(); /* ensure ring space available */ + XENKBD_IN_RING_REF(page, prod) = *event; + xen_wmb(); /* ensure ring contents visible */ + page->in_prod = prod + 1; + return xen_be_send_notify(&xenfb->c.xendev); +} + +/* Send a keyboard (or mouse button) event */ +static int xenfb_send_key(struct XenInput *xenfb, bool down, int keycode) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_KEY; + event.key.pressed = down ? 1 : 0; + event.key.keycode = keycode; + + return xenfb_kbd_event(xenfb, &event); +} + +/* Send a relative mouse movement event */ +static int xenfb_send_motion(struct XenInput *xenfb, + int rel_x, int rel_y, int rel_z) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_MOTION; + event.motion.rel_x = rel_x; + event.motion.rel_y = rel_y; +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207 + event.motion.rel_z = rel_z; +#endif + + return xenfb_kbd_event(xenfb, &event); +} + +/* Send an absolute mouse movement event */ +static int xenfb_send_position(struct XenInput *xenfb, + int abs_x, int abs_y, int z) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_POS; + event.pos.abs_x = abs_x; + event.pos.abs_y = abs_y; +#if __XEN_LATEST_INTERFACE_VERSION__ == 0x00030207 + event.pos.abs_z = z; +#endif +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030208 + event.pos.rel_z = z; +#endif + + return xenfb_kbd_event(xenfb, &event); +} + +/* + * Send a key event from the client to the guest OS + * QEMU gives us a raw scancode from an AT / PS/2 style keyboard. + * We have to turn this into a Linux Input layer keycode. + * + * Extra complexity from the fact that with extended scancodes + * (like those produced by arrow keys) this method gets called + * twice, but we only want to send a single event. So we have to + * track the ''0xe0'' scancode state & collapse the extended keys + * as needed. + * + * Wish we could just send scancodes straight to the guest which + * already has code for dealing with this... + */ +static void xenfb_key_event(void *opaque, int scancode) +{ + struct XenInput *xenfb = opaque; + int down = 1; + + if (scancode == 0xe0) { + xenfb->extended = 1; + return; + } else if (scancode & 0x80) { + scancode &= 0x7f; + down = 0; + } + if (xenfb->extended) { + scancode |= 0x80; + xenfb->extended = 0; + } + xenfb_send_key(xenfb, down, scancode2linux[scancode]); +} + +/* + * Send a mouse event from the client to the guest OS + * + * The QEMU mouse can be in either relative, or absolute mode. + * Movement is sent separately from button state, which has to + * be encoded as virtual key events. We also don''t actually get + * given any button up/down events, so have to track changes in + * the button state. + */ +static void xenfb_mouse_event(void *opaque, + int dx, int dy, int dz, int button_state) +{ + struct XenInput *xenfb = opaque; + int dw = ds_get_width(xenfb->c.ds); + int dh = ds_get_height(xenfb->c.ds); + int i; + + if (xenfb->abs_pointer_wanted) + xenfb_send_position(xenfb, + dx * (dw - 1) / 0x7fff, + dy * (dh - 1) / 0x7fff, + dz); + else + xenfb_send_motion(xenfb, dx, dy, dz); + + for (i = 0 ; i < 8 ; i++) { + int lastDown = xenfb->button_state & (1 << i); + int down = button_state & (1 << i); + if (down == lastDown) + continue; + + if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0) + return; + } + xenfb->button_state = button_state; +} + +static int input_init(struct XenDevice *xendev) +{ + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); + + if (!in->c.ds) { + xen_be_printf(xendev, 1, "ds not set (yet)\n"); + return -1; + } + + xenstore_write_be_int(xendev, "feature-abs-pointer", 1); + return 0; +} + +static int input_connect(struct XenDevice *xendev) +{ + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); + int rc; + + if (xenstore_read_fe_int(xendev, "request-abs-pointer", + &in->abs_pointer_wanted) == -1) + in->abs_pointer_wanted = 0; + + rc = common_bind(&in->c); + if (0 != rc) + return rc; + + qemu_add_kbd_event_handler(xenfb_key_event, in); + in->qmouse = qemu_add_mouse_event_handler(xenfb_mouse_event, in, + in->abs_pointer_wanted, + "Xen PVFB Mouse"); + return 0; +} + +static void input_disconnect(struct XenDevice *xendev) +{ + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); + + if (in->qmouse) { + qemu_remove_mouse_event_handler(in->qmouse); + in->qmouse = NULL; + } + qemu_add_kbd_event_handler(NULL, NULL); + common_unbind(&in->c); +} + +static void input_event(struct XenDevice *xendev) +{ + struct XenInput *xenfb = container_of(xendev, struct XenInput, c.xendev); + struct xenkbd_page *page = xenfb->c.page; + + /* We don''t understand any keyboard events, so just ignore them. */ + if (page->out_prod == page->out_cons) + return; + page->out_cons = page->out_prod; + xen_be_send_notify(&xenfb->c.xendev); +} + +/* -------------------------------------------------------------------- */ + +static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src) +{ + uint32_t *src32 = src; + uint64_t *src64 = src; + int i; + + for (i = 0; i < count; i++) + dst[i] = (mode == 32) ? src32[i] : src64[i]; +} + +static int xenfb_map_fb(struct XenFB *xenfb) +{ + struct xenfb_page *page = xenfb->c.page; + char *protocol = xenfb->c.xendev.protocol; + int n_fbdirs; + unsigned long *pgmfns = NULL; + unsigned long *fbmfns = NULL; + void *map, *pd; + int mode, ret = -1; + + /* default to native */ + pd = page->pd; + mode = sizeof(unsigned long) * 8; + + if (!protocol) { + /* + * Undefined protocol, some guesswork needed. + * + * Old frontends which don''t set the protocol use + * one page directory only, thus pd[1] must be zero. + * pd[1] of the 32bit struct layout and the lower + * 32 bits of pd[0] of the 64bit struct layout have + * the same location, so we can check that ... + */ + uint32_t *ptr32 = NULL; + uint32_t *ptr64 = NULL; +#if defined(__i386__) + ptr32 = (void*)page->pd; + ptr64 = ((void*)page->pd) + 4; +#elif defined(__x86_64__) + ptr32 = ((void*)page->pd) - 4; + ptr64 = (void*)page->pd; +#endif + if (ptr32) { + if (ptr32[1] == 0) { + mode = 32; + pd = ptr32; + } else { + mode = 64; + pd = ptr64; + } + } +#if defined(__x86_64__) + } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { + /* 64bit dom0, 32bit domU */ + mode = 32; + pd = ((void*)page->pd) - 4; +#elif defined(__i386__) + } else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { + /* 32bit dom0, 64bit domU */ + mode = 64; + pd = ((void*)page->pd) + 4; +#endif + } + + if (xenfb->pixels) { + munmap(xenfb->pixels, xenfb->fbpages * XC_PAGE_SIZE); + xenfb->pixels = NULL; + } + + xenfb->fbpages = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + n_fbdirs = xenfb->fbpages * mode / 8; + n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + + pgmfns = qemu_mallocz(sizeof(unsigned long) * n_fbdirs); + fbmfns = qemu_mallocz(sizeof(unsigned long) * xenfb->fbpages); + + xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd); + map = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, + PROT_READ, pgmfns, n_fbdirs); + if (map == NULL) + goto out; + xenfb_copy_mfns(mode, xenfb->fbpages, fbmfns, map); + munmap(map, n_fbdirs * XC_PAGE_SIZE); + + xenfb->pixels = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, + PROT_READ | PROT_WRITE, fbmfns, xenfb->fbpages); + if (xenfb->pixels == NULL) + goto out; + + ret = 0; /* all is fine */ + +out: + qemu_free(pgmfns); + qemu_free(fbmfns); + return ret; +} + +static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim, + int width, int height, int depth, + size_t fb_len, int offset, int row_stride) +{ + size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd); + size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz; + size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz; + size_t fb_len_max = fb_pages * XC_PAGE_SIZE; + int max_width, max_height; + + if (fb_len_lim > fb_len_max) { + xen_be_printf(&xenfb->c.xendev, 0, "fb size limit %zu exceeds %zu, corrected\n", + fb_len_lim, fb_len_max); + fb_len_lim = fb_len_max; + } + if (fb_len_lim && fb_len > fb_len_lim) { + xen_be_printf(&xenfb->c.xendev, 0, "frontend fb size %zu limited to %zu\n", + fb_len, fb_len_lim); + fb_len = fb_len_lim; + } + if (depth != 8 && depth != 16 && depth != 24 && depth != 32) { + xen_be_printf(&xenfb->c.xendev, 0, "can''t handle frontend fb depth %d\n", + depth); + return -1; + } + if (row_stride <= 0 || row_stride > fb_len) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n", row_stride); + return -1; + } + max_width = row_stride / (depth / 8); + if (width < 0 || width > max_width) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend width %d limited to %d\n", + width, max_width); + width = max_width; + } + if (offset < 0 || offset >= fb_len) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend offset %d (max %zu)\n", + offset, fb_len - 1); + return -1; + } + max_height = (fb_len - offset) / row_stride; + if (height < 0 || height > max_height) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend height %d limited to %d\n", + height, max_height); + height = max_height; + } + xenfb->fb_len = fb_len; + xenfb->row_stride = row_stride; + xenfb->depth = depth; + xenfb->width = width; + xenfb->height = height; + xenfb->offset = offset; + xenfb->up_fullscreen = 1; + xenfb->do_resize = 1; + xen_be_printf(&xenfb->c.xendev, 1, "framebuffer %dx%dx%d offset %d stride %d\n", + width, height, depth, offset, row_stride); + return 0; +} + +/* A convenient function for munging pixels between different depths */ +#define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \ + for (line = y ; line < (y+h) ; line++) { \ + SRC_T *src = (SRC_T *)(xenfb->pixels \ + + xenfb->offset \ + + (line * xenfb->row_stride) \ + + (x * xenfb->depth / 8)); \ + DST_T *dst = (DST_T *)(data \ + + (line * linesize) \ + + (x * bpp / 8)); \ + int col; \ + const int RSS = 32 - (RSB + GSB + BSB); \ + const int GSS = 32 - (GSB + BSB); \ + const int BSS = 32 - (BSB); \ + const uint32_t RSM = (~0U) << (32 - RSB); \ + const uint32_t GSM = (~0U) << (32 - GSB); \ + const uint32_t BSM = (~0U) << (32 - BSB); \ + const int RDS = 32 - (RDB + GDB + BDB); \ + const int GDS = 32 - (GDB + BDB); \ + const int BDS = 32 - (BDB); \ + const uint32_t RDM = (~0U) << (32 - RDB); \ + const uint32_t GDM = (~0U) << (32 - GDB); \ + const uint32_t BDM = (~0U) << (32 - BDB); \ + for (col = x ; col < (x+w) ; col++) { \ + uint32_t spix = *src; \ + *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \ + (((spix << GSS) & GSM & GDM) >> GDS) | \ + (((spix << BSS) & BSM & BDM) >> BDS); \ + src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \ + dst = (DST_T *) ((unsigned long) dst + bpp / 8); \ + } \ + } + + +/* + * This copies data from the guest framebuffer region, into QEMU''s + * displaysurface. qemu uses 16 or 32 bpp. In case the pv framebuffer + * uses something else we must convert and copy, otherwise we can + * supply the buffer directly and no thing here. + */ +static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h) +{ + int line, oops = 0; + int bpp = ds_get_bits_per_pixel(xenfb->c.ds); + int linesize = ds_get_linesize(xenfb->c.ds); + uint8_t *data = ds_get_data(xenfb->c.ds); + + if (!is_buffer_shared(xenfb->c.ds->surface)) { + switch (xenfb->depth) { + case 8: + if (bpp == 16) { + BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5); + } else if (bpp == 32) { + BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8); + } else { + oops = 1; + } + break; + case 24: + if (bpp == 16) { + BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5); + } else if (bpp == 32) { + BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8); + } else { + oops = 1; + } + break; + default: + oops = 1; + } + } + if (oops) /* should not happen */ + xen_be_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n", + __FUNCTION__, xenfb->depth, bpp); + + dpy_update(xenfb->c.ds, x, y, w, h); +} + +#ifdef XENFB_TYPE_REFRESH_PERIOD +static int xenfb_queue_full(struct XenFB *xenfb) +{ + struct xenfb_page *page = xenfb->c.page; + uint32_t cons, prod; + + if (!page) + return 1; + + prod = page->in_prod; + cons = page->in_cons; + return prod - cons == XENFB_IN_RING_LEN; +} + +static void xenfb_send_event(struct XenFB *xenfb, union xenfb_in_event *event) +{ + uint32_t prod; + struct xenfb_page *page = xenfb->c.page; + + prod = page->in_prod; + /* caller ensures !xenfb_queue_full() */ + xen_mb(); /* ensure ring space available */ + XENFB_IN_RING_REF(page, prod) = *event; + xen_wmb(); /* ensure ring contents visible */ + page->in_prod = prod + 1; + + xen_be_send_notify(&xenfb->c.xendev); +} + +static void xenfb_send_refresh_period(struct XenFB *xenfb, int period) +{ + union xenfb_in_event event; + + memset(&event, 0, sizeof(event)); + event.type = XENFB_TYPE_REFRESH_PERIOD; + event.refresh_period.period = period; + xenfb_send_event(xenfb, &event); +} +#endif + +/* + * Periodic update of display. + * Also transmit the refresh interval to the frontend. + * + * Never ever do any qemu display operations + * (resize, screen update) outside this function. + * Our screen might be inactive. When asked for + * an update we know it is active. + */ +static void xenfb_update(void *opaque) +{ + struct XenFB *xenfb = opaque; + struct DisplayChangeListener *l; + int dw = ds_get_width(xenfb->c.ds); + int dh = ds_get_height(xenfb->c.ds); + int i; + + if (xenfb->c.xendev.be_state != XenbusStateConnected) + return; + + if (xenfb->feature_update) { +#ifdef XENFB_TYPE_REFRESH_PERIOD + int period = 99999999; + int idle = 1; + + if (xenfb_queue_full(xenfb)) + return; + + for (l = xenfb->c.ds->listeners; l != NULL; l = l->next) { + if (l->idle) + continue; + idle = 0; + if (!l->gui_timer_interval) { + if (period > GUI_REFRESH_INTERVAL) + period = GUI_REFRESH_INTERVAL; + } else { + if (period > l->gui_timer_interval) + period = l->gui_timer_interval; + } + } + if (idle) + period = XENFB_NO_REFRESH; + + if (xenfb->refresh_period != period) { + xenfb_send_refresh_period(xenfb, period); + xenfb->refresh_period = period; + xen_be_printf(&xenfb->c.xendev, 1, "refresh period: %d\n", period); + } +#else + ; /* nothing */ +#endif + } else { + /* we don''t get update notifications, thus use the + * sledge hammer approach ... */ + xenfb->up_fullscreen = 1; + } + + /* resize if needed */ + if (xenfb->do_resize) { + xenfb->do_resize = 0; + switch (xenfb->depth) { + case 16: + case 32: + /* console.c supported depth -> buffer can be used directly */ + qemu_free_displaysurface(xenfb->c.ds); + xenfb->c.ds->surface = qemu_create_displaysurface_from + (xenfb->width, xenfb->height, xenfb->depth, + xenfb->row_stride, xenfb->pixels + xenfb->offset); + break; + default: + /* we must convert stuff */ + qemu_resize_displaysurface(xenfb->c.ds, + xenfb->width, xenfb->height); + break; + } + xen_be_printf(&xenfb->c.xendev, 1, "update: resizing: %dx%d @ %d bpp%s\n", + xenfb->width, xenfb->height, xenfb->depth, + is_buffer_shared(xenfb->c.ds->surface) ? " (shared)" : ""); + dpy_resize(xenfb->c.ds); + xenfb->up_fullscreen = 1; + } + + /* run queued updates */ + if (xenfb->up_fullscreen) { + xen_be_printf(&xenfb->c.xendev, 3, "update: fullscreen\n"); + xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height); + } else if (xenfb->up_count) { + xen_be_printf(&xenfb->c.xendev, 3, "update: %d rects\n", xenfb->up_count); + for (i = 0; i < xenfb->up_count; i++) + xenfb_guest_copy(xenfb, + xenfb->up_rects[i].x, + xenfb->up_rects[i].y, + xenfb->up_rects[i].w, + xenfb->up_rects[i].h); + } else { + xen_be_printf(&xenfb->c.xendev, 3, "update: nothing\n"); + } + xenfb->up_count = 0; + xenfb->up_fullscreen = 0; +} + +/* QEMU display state changed, so refresh the framebuffer copy */ +static void xenfb_invalidate(void *opaque) +{ + struct XenFB *xenfb = opaque; + xenfb->up_fullscreen = 1; +} + +static void xenfb_handle_events(struct XenFB *xenfb) +{ + uint32_t prod, cons; + struct xenfb_page *page = xenfb->c.page; + + prod = page->out_prod; + if (prod == page->out_cons) + return; + xen_rmb(); /* ensure we see ring contents up to prod */ + for (cons = page->out_cons; cons != prod; cons++) { + union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons); + int x, y, w, h; + + switch (event->type) { + case XENFB_TYPE_UPDATE: + if (xenfb->up_count == UP_QUEUE) + xenfb->up_fullscreen = 1; + if (xenfb->up_fullscreen) + break; + x = MAX(event->update.x, 0); + y = MAX(event->update.y, 0); + w = MIN(event->update.width, xenfb->width - x); + h = MIN(event->update.height, xenfb->height - y); + if (w < 0 || h < 0) { + fprintf(stderr, "xen be: %s: bogus update ignored\n", + xenfb->c.xendev.name); + break; + } + if (x != event->update.x || y != event->update.y + || w != event->update.width + || h != event->update.height) { + fprintf(stderr, "xen be: %s: bogus update clipped\n", + xenfb->c.xendev.name); + } + if (w == xenfb->width && h > xenfb->height / 2) { + /* scroll detector: updated more than 50% of the lines, + * don''t bother keeping track of the rectangles then */ + xenfb->up_fullscreen = 1; + } else { + xenfb->up_rects[xenfb->up_count].x = x; + xenfb->up_rects[xenfb->up_count].y = y; + xenfb->up_rects[xenfb->up_count].w = w; + xenfb->up_rects[xenfb->up_count].h = h; + xenfb->up_count++; + } + break; +#ifdef XENFB_TYPE_RESIZE + case XENFB_TYPE_RESIZE: + if (xenfb_configure_fb(xenfb, xenfb->fb_len, + event->resize.width, + event->resize.height, + event->resize.depth, + xenfb->fb_len, + event->resize.offset, + event->resize.stride) < 0) + break; + xenfb_invalidate(xenfb); + break; +#endif + } + } + xen_mb(); /* ensure we''re done with ring contents */ + page->out_cons = cons; +} + +static int fb_init(struct XenDevice *xendev) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + + fb->refresh_period = -1; + +#ifdef XENFB_TYPE_RESIZE + xenstore_write_be_int(xendev, "feature-resize", 1); +#endif + return 0; +} + +static int fb_connect(struct XenDevice *xendev) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + struct xenfb_page *fb_page; + int videoram; + int rc; + + if (xenstore_read_fe_int(xendev, "videoram", &videoram) == -1) + videoram = 0; + + rc = common_bind(&fb->c); + if (0 != rc) + return rc; + + fb_page = fb->c.page; + rc = xenfb_configure_fb(fb, videoram * 1024 * 1024U, + fb_page->width, fb_page->height, fb_page->depth, + fb_page->mem_length, 0, fb_page->line_length); + if (0 != rc) + return rc; + + rc = xenfb_map_fb(fb); + if (0 != rc) + return rc; + +#if 0 /* handled in xen_init_display() for now */ + if (!fb->have_console) { + fb->c.ds = graphic_console_init(xenfb_update, + xenfb_invalidate, + NULL, + NULL, + fb); + fb->have_console = 1; + } +#endif + + if (xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update) == -1) + fb->feature_update = 0; + if (fb->feature_update) + xenstore_write_be_int(xendev, "request-update", 1); + + xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n", + fb->feature_update, videoram); + return 0; +} + +static void fb_disconnect(struct XenDevice *xendev) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + + /* + * FIXME: qemu can''t un-init gfx display (yet?). + * Replacing the framebuffer with anonymous shared memory + * instead. This releases the guest pages and keeps qemu happy. + */ + fb->pixels = mmap(fb->pixels, fb->fbpages * XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, + -1, 0); + common_unbind(&fb->c); + fb->feature_update = 0; + fb->bug_trigger = 0; +} + +static void fb_frontend_changed(struct XenDevice *xendev, const char *node) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + + /* + * Set state to Connected *again* once the frontend switched + * to connected. We must trigger the watch a second time to + * workaround a frontend bug. + */ + if (fb->bug_trigger == 0 && strcmp(node, "state") == 0 && + xendev->fe_state == XenbusStateConnected && + xendev->be_state == XenbusStateConnected) { + xen_be_printf(xendev, 2, "re-trigger connected (frontend bug)\n"); + xen_be_set_state(xendev, XenbusStateConnected); + fb->bug_trigger = 1; /* only once */ + } +} + +static void fb_event(struct XenDevice *xendev) +{ + struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev); + + xenfb_handle_events(xenfb); + xen_be_send_notify(&xenfb->c.xendev); +} + +/* -------------------------------------------------------------------- */ + +struct XenDevOps xen_kbdmouse_ops = { + .size = sizeof(struct XenInput), + .init = input_init, + .connect = input_connect, + .disconnect = input_disconnect, + .event = input_event, +}; + +struct XenDevOps xen_framebuffer_ops = { + .size = sizeof(struct XenFB), + .init = fb_init, + .connect = fb_connect, + .disconnect = fb_disconnect, + .event = fb_event, + .frontend_changed = fb_frontend_changed, +}; + +/* + * FIXME/TODO: Kill this. + * Temporary needed while DisplayState reorganization is in flight. + */ +void xen_init_display(int domid) +{ + struct XenDevice *xfb, *xin; + struct XenFB *fb; + struct XenInput *in; + int i = 0; + +wait_more: + i++; + main_loop_wait(10); /* miliseconds */ + xfb = xen_be_find_xendev("vfb", domid, 0); + xin = xen_be_find_xendev("vkbd", domid, 0); + if (!xfb || !xin) { + if (i < 256) + goto wait_more; + fprintf(stderr, "%s: displaystate setup failed\n", __FUNCTION__); + return; + } + + /* vfb */ + fb = container_of(xfb, struct XenFB, c.xendev); + fb->c.ds = graphic_console_init(xenfb_update, + xenfb_invalidate, + NULL, + NULL, + fb); + fb->have_console = 1; + + /* vkbd */ + in = container_of(xin, struct XenInput, c.xendev); + in->c.ds = fb->c.ds; + + /* retry ->init() */ + xen_be_check_state(xin); + xen_be_check_state(xfb); +} -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-07 14:44 UTC
[Xen-devel] [PATCH 05/10] xen: add block device backend driver.
This patch adds a block device backend driver to qemu. It is a pure userspace implemention using the gntdev interface. It uses "qdisk" as backend name in xenstore so it doesn''t interfere with the other existing backends (blkback aka "vbd" and tapdisk aka "tap"). Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen_backend.h | 2 + hw/xen_blkif.h | 103 +++++++ hw/xen_disk.c | 777 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_machine_pv.c | 1 + sysemu.h | 2 +- vl.c | 6 +- 7 files changed, 890 insertions(+), 3 deletions(-) create mode 100644 hw/xen_blkif.h create mode 100644 hw/xen_disk.c diff --git a/Makefile.target b/Makefile.target index ac082d2..edbc928 100644 --- a/Makefile.target +++ b/Makefile.target @@ -559,7 +559,7 @@ endif # xen backend driver support XEN_OBJS := xen_machine_pv.o xen_backend.o -XEN_OBJS += xen_console.o xenfb.o +XEN_OBJS += xen_console.o xenfb.o xen_disk.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.h b/hw/xen_backend.h index e9a4e2d..dd426dd 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -2,6 +2,7 @@ #define QEMU_HW_XEN_BACKEND_H 1 #include "xen_common.h" +#include "sysemu.h" /* ------------------------------------------------------------- */ @@ -87,6 +88,7 @@ void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ... extern struct XenDevOps xen_console_ops; /* xen_console.c */ extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ +extern struct XenDevOps xen_blkdev_ops; /* xen_disk.c */ void xen_init_display(int domid); diff --git a/hw/xen_blkif.h b/hw/xen_blkif.h new file mode 100644 index 0000000..254a5fd --- /dev/null +++ b/hw/xen_blkif.h @@ -0,0 +1,103 @@ +#ifndef __XEN_BLKIF_H__ +#define __XEN_BLKIF_H__ + +#include <xen/io/ring.h> +#include <xen/io/blkif.h> +#include <xen/io/protocols.h> + +/* Not a real protocol. Used to generate ring structs which contain + * the elements common to all protocols only. This way we get a + * compiler-checkable way to use common struct elements, so we can + * avoid using switch(protocol) in a number of places. */ +struct blkif_common_request { + char dummy; +}; +struct blkif_common_response { + char dummy; +}; + +/* i386 protocol version */ +#pragma pack(push, 4) +struct blkif_x86_32_request { + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t id; /* private guest value, echoed in resp */ + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; +struct blkif_x86_32_response { + uint64_t id; /* copied from request */ + uint8_t operation; /* copied from request */ + int16_t status; /* BLKIF_RSP_??? */ +}; +typedef struct blkif_x86_32_request blkif_x86_32_request_t; +typedef struct blkif_x86_32_response blkif_x86_32_response_t; +#pragma pack(pop) + +/* x86_64 protocol version */ +struct blkif_x86_64_request { + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t __attribute__((__aligned__(8))) id; + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; +struct blkif_x86_64_response { + uint64_t __attribute__((__aligned__(8))) id; + uint8_t operation; /* copied from request */ + int16_t status; /* BLKIF_RSP_??? */ +}; +typedef struct blkif_x86_64_request blkif_x86_64_request_t; +typedef struct blkif_x86_64_response blkif_x86_64_response_t; + +DEFINE_RING_TYPES(blkif_common, struct blkif_common_request, struct blkif_common_response); +DEFINE_RING_TYPES(blkif_x86_32, struct blkif_x86_32_request, struct blkif_x86_32_response); +DEFINE_RING_TYPES(blkif_x86_64, struct blkif_x86_64_request, struct blkif_x86_64_response); + +union blkif_back_rings { + blkif_back_ring_t native; + blkif_common_back_ring_t common; + blkif_x86_32_back_ring_t x86_32; + blkif_x86_64_back_ring_t x86_64; +}; +typedef union blkif_back_rings blkif_back_rings_t; + +enum blkif_protocol { + BLKIF_PROTOCOL_NATIVE = 1, + BLKIF_PROTOCOL_X86_32 = 2, + BLKIF_PROTOCOL_X86_64 = 3, +}; + +static void inline blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_request_t *src) +{ + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; + + dst->operation = src->operation; + dst->nr_segments = src->nr_segments; + dst->handle = src->handle; + dst->id = src->id; + dst->sector_number = src->sector_number; + if (n > src->nr_segments) + n = src->nr_segments; + for (i = 0; i < n; i++) + dst->seg[i] = src->seg[i]; +} + +static void inline blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_request_t *src) +{ + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; + + dst->operation = src->operation; + dst->nr_segments = src->nr_segments; + dst->handle = src->handle; + dst->id = src->id; + dst->sector_number = src->sector_number; + if (n > src->nr_segments) + n = src->nr_segments; + for (i = 0; i < n; i++) + dst->seg[i] = src->seg[i]; +} + +#endif /* __XEN_BLKIF_H__ */ diff --git a/hw/xen_disk.c b/hw/xen_disk.c new file mode 100644 index 0000000..c153628 --- /dev/null +++ b/hw/xen_disk.c @@ -0,0 +1,777 @@ +/* + * xen paravirt block device backend + * + * (c) Gerd Hoffmann <kraxel@redhat.com> + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <inttypes.h> +#include <time.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/uio.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> + +#include "hw.h" +#include "block_int.h" +#include "qemu-char.h" +#include "xen_blkif.h" +#include "xen_backend.h" + +/* ------------------------------------------------------------- */ + +static int syncwrite = 0; +static int batch_maps = 0; + +static int max_requests = 32; +static int use_aio = 1; + +/* ------------------------------------------------------------- */ + +#define BLOCK_SIZE 512 +#define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2) + +struct ioreq { + blkif_request_t req; + int16_t status; + + /* parsed request */ + off_t start; + QEMUIOVector v; + int presync; + int postsync; + + /* grant mapping */ + uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + int prot; + void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + void *pages; + + /* aio status */ + int aio_inflight; + int aio_errors; + + struct XenBlkDev *blkdev; + LIST_ENTRY(ioreq) list; +}; + +struct XenBlkDev { + struct XenDevice xendev; /* must be first */ + char *params; + char *mode; + char *type; + char *dev; + char *devtype; + const char *fileproto; + const char *filename; + int ring_ref; + void *sring; + int64_t file_blk; + int64_t file_size; + int protocol; + blkif_back_rings_t rings; + int more_work; + int cnt_map; + + /* request lists */ + LIST_HEAD(inflight_head, ioreq) inflight; + LIST_HEAD(finished_head, ioreq) finished; + LIST_HEAD(freelist_head, ioreq) freelist; + int requests_total; + int requests_inflight; + int requests_finished; + + /* qemu block driver */ + int index; + BlockDriverState *bs; + QEMUBH *bh; +}; + +/* ------------------------------------------------------------- */ + +static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) +{ + struct ioreq *ioreq = NULL; + + if (LIST_EMPTY(&blkdev->freelist)) { + if (blkdev->requests_total >= max_requests) + goto out; + /* allocate new struct */ + ioreq = qemu_mallocz(sizeof(*ioreq)); + if (ioreq == NULL) + goto out; + ioreq->blkdev = blkdev; + blkdev->requests_total++; + qemu_iovec_init(&ioreq->v, BLKIF_MAX_SEGMENTS_PER_REQUEST); + } else { + /* get one from freelist */ + ioreq = LIST_FIRST(&blkdev->freelist); + LIST_REMOVE(ioreq, list); + qemu_iovec_reset(&ioreq->v); + } + LIST_INSERT_HEAD(&blkdev->inflight, ioreq, list); + blkdev->requests_inflight++; + +out: + return ioreq; +} + +static void ioreq_finish(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + + LIST_REMOVE(ioreq, list); + LIST_INSERT_HEAD(&blkdev->finished, ioreq, list); + blkdev->requests_inflight--; + blkdev->requests_finished++; +} + +static void ioreq_release(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + + LIST_REMOVE(ioreq, list); + memset(ioreq, 0, sizeof(*ioreq)); + ioreq->blkdev = blkdev; + LIST_INSERT_HEAD(&blkdev->freelist, ioreq, list); + blkdev->requests_finished--; +} + +/* + * translate request into iovec + start offset + * do sanity checks along the way + */ +static int ioreq_parse(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + uintptr_t mem; + size_t len; + int i; + + xen_be_printf(&blkdev->xendev, 3, + "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", + ioreq->req.operation, ioreq->req.nr_segments, + ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + ioreq->prot = PROT_WRITE; /* to memory */ + if (BLKIF_OP_READ != ioreq->req.operation && blkdev->mode[0] != ''w'') { + xen_be_printf(&blkdev->xendev, 0, "error: write req for ro device\n"); + goto err; + } + break; + case BLKIF_OP_WRITE_BARRIER: + if (!syncwrite) + ioreq->presync = ioreq->postsync = 1; + /* fall through */ + case BLKIF_OP_WRITE: + ioreq->prot = PROT_READ; /* from memory */ + if (syncwrite) + ioreq->postsync = 1; + break; + default: + xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", + ioreq->req.operation); + goto err; + }; + + ioreq->start = ioreq->req.sector_number * blkdev->file_blk; + for (i = 0; i < ioreq->req.nr_segments; i++) { + if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { + xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); + goto err; + } + if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { + xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n"); + goto err; + } + if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { + xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n"); + goto err; + } + + ioreq->domids[i] = blkdev->xendev.dom; + ioreq->refs[i] = ioreq->req.seg[i].gref; + + mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; + len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; + qemu_iovec_add(&ioreq->v, (void*)mem, len); + } + if (ioreq->start + ioreq->v.size > blkdev->file_size) { + xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); + goto err; + } + return 0; + +err: + ioreq->status = BLKIF_RSP_ERROR; + return -1; +} + +static void ioreq_unmap(struct ioreq *ioreq) +{ + int gnt = ioreq->blkdev->xendev.gnttabdev; + int i; + + if (ioreq->v.niov == 0) + return; + if (batch_maps) { + if (!ioreq->pages) + return; + if (0 != xc_gnttab_munmap(gnt, ioreq->pages, ioreq->v.niov)) + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + ioreq->blkdev->cnt_map -= ioreq->v.niov; + ioreq->pages = NULL; + } else { + for (i = 0; i < ioreq->v.niov; i++) { + if (!ioreq->page[i]) + continue; + if (0 != xc_gnttab_munmap(gnt, ioreq->page[i], 1)) + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + ioreq->blkdev->cnt_map--; + ioreq->page[i] = NULL; + } + } +} + +static int ioreq_map(struct ioreq *ioreq) +{ + int gnt = ioreq->blkdev->xendev.gnttabdev; + int i; + + if (ioreq->v.niov == 0) + return 0; + if (batch_maps) { + ioreq->pages = xc_gnttab_map_grant_refs + (gnt, ioreq->v.niov, ioreq->domids, ioreq->refs, ioreq->prot); + if (ioreq->pages == NULL) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can''t map %d grant refs (%s, %d maps)\n", + ioreq->v.niov, strerror(errno), ioreq->blkdev->cnt_map); + return -1; + } + for (i = 0; i < ioreq->v.niov; i++) + ioreq->v.iov[i].iov_base = ioreq->pages + i * XC_PAGE_SIZE + + (uintptr_t)ioreq->v.iov[i].iov_base; + ioreq->blkdev->cnt_map += ioreq->v.niov; + } else { + for (i = 0; i < ioreq->v.niov; i++) { + ioreq->page[i] = xc_gnttab_map_grant_ref + (gnt, ioreq->domids[i], ioreq->refs[i], ioreq->prot); + if (ioreq->page[i] == NULL) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can''t map grant ref %d (%s, %d maps)\n", + ioreq->refs[i], strerror(errno), ioreq->blkdev->cnt_map); + ioreq_unmap(ioreq); + return -1; + } + ioreq->v.iov[i].iov_base = ioreq->page[i] + (uintptr_t)ioreq->v.iov[i].iov_base; + ioreq->blkdev->cnt_map++; + } + } + return 0; +} + +static int ioreq_runio_qemu_sync(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + int i, rc, len = 0; + off_t pos; + + if (ioreq_map(ioreq) == -1) + goto err; + if (ioreq->presync) + bdrv_flush(blkdev->bs); + + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + pos = ioreq->start; + for (i = 0; i < ioreq->v.niov; i++) { + rc = bdrv_read(blkdev->bs, pos / BLOCK_SIZE, + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "rd I/O error (%p, len %zd)\n", + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len); + goto err; + } + len += ioreq->v.iov[i].iov_len; + pos += ioreq->v.iov[i].iov_len; + } + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_WRITE_BARRIER: + pos = ioreq->start; + for (i = 0; i < ioreq->v.niov; i++) { + rc = bdrv_write(blkdev->bs, pos / BLOCK_SIZE, + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "wr I/O error (%p, len %zd)\n", + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len); + goto err; + } + len += ioreq->v.iov[i].iov_len; + pos += ioreq->v.iov[i].iov_len; + } + break; + default: + /* unknown operation (shouldn''t happen -- parse catches this) */ + goto err; + } + + if (ioreq->postsync) + bdrv_flush(blkdev->bs); + ioreq->status = BLKIF_RSP_OKAY; + + ioreq_unmap(ioreq); + ioreq_finish(ioreq); + return 0; + +err: + ioreq->status = BLKIF_RSP_ERROR; + return -1; +} + +static void qemu_aio_complete(void *opaque, int ret) +{ + struct ioreq *ioreq = opaque; + + if (ret != 0) { + xen_be_printf(&ioreq->blkdev->xendev, 0, "%s I/O error\n", + ioreq->req.operation == BLKIF_OP_READ ? "read" : "write"); + ioreq->aio_errors++; + } + + ioreq->aio_inflight--; + if (ioreq->aio_inflight > 0) + return; + + ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; + ioreq_unmap(ioreq); + ioreq_finish(ioreq); + qemu_bh_schedule(ioreq->blkdev->bh); +} + +static int ioreq_runio_qemu_aio(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + + if (ioreq_map(ioreq) == -1) + goto err; + + ioreq->aio_inflight++; + if (ioreq->presync) + bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */ + + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + ioreq->aio_inflight++; + bdrv_aio_readv(blkdev->bs, ioreq->start / BLOCK_SIZE, + &ioreq->v, ioreq->v.size / BLOCK_SIZE, + qemu_aio_complete, ioreq); + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_WRITE_BARRIER: + ioreq->aio_inflight++; + bdrv_aio_writev(blkdev->bs, ioreq->start / BLOCK_SIZE, + &ioreq->v, ioreq->v.size / BLOCK_SIZE, + qemu_aio_complete, ioreq); + break; + default: + /* unknown operation (shouldn''t happen -- parse catches this) */ + goto err; + } + + if (ioreq->postsync) + bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */ + qemu_aio_complete(ioreq, 0); + + return 0; + +err: + ioreq->status = BLKIF_RSP_ERROR; + return -1; +} + +static int blk_send_response_one(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + int send_notify = 0; + int have_requests = 0; + blkif_response_t resp; + void *dst; + + resp.id = ioreq->req.id; + resp.operation = ioreq->req.operation; + resp.status = ioreq->status; + + /* Place on the response ring for the relevant domain. */ + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + dst = RING_GET_RESPONSE(&blkdev->rings.native, blkdev->rings.native.rsp_prod_pvt); + break; + case BLKIF_PROTOCOL_X86_32: + dst = RING_GET_RESPONSE(&blkdev->rings.x86_32, blkdev->rings.x86_32.rsp_prod_pvt); + break; + case BLKIF_PROTOCOL_X86_64: + dst = RING_GET_RESPONSE(&blkdev->rings.x86_64, blkdev->rings.x86_64.rsp_prod_pvt); + break; + default: + dst = NULL; + } + memcpy(dst, &resp, sizeof(resp)); + blkdev->rings.common.rsp_prod_pvt++; + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify); + if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) { + /* + * Tail check for pending requests. Allows frontend to avoid + * notifications if requests are already in flight (lower + * overheads and promotes batching). + */ + RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests); + } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) { + have_requests = 1; + } + + if (have_requests) + blkdev->more_work++; + return send_notify; +} + +/* walk finished list, send outstanding responses, free requests */ +static void blk_send_response_all(struct XenBlkDev *blkdev) +{ + struct ioreq *ioreq; + int send_notify = 0; + + while (!LIST_EMPTY(&blkdev->finished)) { + ioreq = LIST_FIRST(&blkdev->finished); + send_notify += blk_send_response_one(ioreq); + ioreq_release(ioreq); + } + if (send_notify) + xen_be_send_notify(&blkdev->xendev); +} + +static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_IDX rc) +{ + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc), + sizeof(ioreq->req)); + break; + case BLKIF_PROTOCOL_X86_32: + blkif_get_x86_32_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_32, rc)); + break; + case BLKIF_PROTOCOL_X86_64: + blkif_get_x86_64_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_64, rc)); + break; + } + return 0; +} + +static void blk_handle_requests(struct XenBlkDev *blkdev) +{ + RING_IDX rc, rp; + struct ioreq *ioreq; + + blkdev->more_work = 0; + + rc = blkdev->rings.common.req_cons; + rp = blkdev->rings.common.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + if (use_aio) + blk_send_response_all(blkdev); + while ((rc != rp)) { + /* pull request from ring */ + if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) + break; + ioreq = ioreq_start(blkdev); + if (ioreq == NULL) { + blkdev->more_work++; + break; + } + blk_get_request(blkdev, ioreq, rc); + blkdev->rings.common.req_cons = ++rc; + + /* parse them */ + if (0 != ioreq_parse(ioreq)) { + if (blk_send_response_one(ioreq)) + xen_be_send_notify(&blkdev->xendev); + ioreq_release(ioreq); + continue; + } + + if (use_aio) { + /* run i/o in aio mode */ + ioreq_runio_qemu_aio(ioreq); + } else { + /* run i/o in sync mode */ + ioreq_runio_qemu_sync(ioreq); + } + } + if (!use_aio) + blk_send_response_all(blkdev); + + if (blkdev->more_work && blkdev->requests_inflight < max_requests) + qemu_bh_schedule(blkdev->bh); +} + +/* ------------------------------------------------------------- */ + +static void blk_bh(void *opaque) +{ + struct XenBlkDev *blkdev = opaque; + blk_handle_requests(blkdev); +} + +static void blk_alloc(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + LIST_INIT(&blkdev->inflight); + LIST_INIT(&blkdev->finished); + LIST_INIT(&blkdev->freelist); + blkdev->bh = qemu_bh_new(blk_bh, blkdev); + if (xen_mode != XEN_EMULATE) + batch_maps = 1; +} + +static int blk_init(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + int mode, qflags, have_barriers, info = 0; + char *h; + + /* read xenstore entries */ + if (blkdev->params == NULL) { + blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); + if (NULL != (h = strchr(blkdev->params, '':''))) { + blkdev->fileproto = blkdev->params; + blkdev->filename = h+1; + *h = 0; + } else { + blkdev->fileproto = "<unset>"; + blkdev->filename = blkdev->params; + } + } + if (blkdev->mode == NULL) + blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); + if (blkdev->type == NULL) + blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); + if (blkdev->dev == NULL) + blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); + if (blkdev->devtype == NULL) + blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type"); + + /* do we have all we need? */ + if (blkdev->params == NULL || + blkdev->mode == NULL || + blkdev->type == NULL || + blkdev->dev == NULL) + return -1; + + /* read-only ? */ + if (strcmp(blkdev->mode, "w") == 0) { + mode = O_RDWR; + qflags = BDRV_O_RDWR; + } else { + mode = O_RDONLY; + qflags = BDRV_O_RDONLY; + info |= VDISK_READONLY; + } + + /* cdrom ? */ + if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom")) + info |= VDISK_CDROM; + + /* init qemu block driver */ + blkdev->index = (blkdev->xendev.dev - 202 * 256) / 16; + blkdev->index = drive_get_index(IF_XEN, 0, blkdev->index); + if (blkdev->index == -1) { + /* setup via xenbus -> create new block driver instance */ + xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); + blkdev->bs = bdrv_new(blkdev->dev); + if (blkdev->bs) { + if (0 != bdrv_open2(blkdev->bs, blkdev->filename, qflags, + bdrv_find_format(blkdev->fileproto))) { + bdrv_delete(blkdev->bs); + blkdev->bs = NULL; + } + } + if (!blkdev->bs) + return -1; + } else { + /* setup via qemu cmdline -> already setup for us */ + xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n"); + blkdev->bs = drives_table[blkdev->index].bdrv; + } + blkdev->file_blk = BLOCK_SIZE; + blkdev->file_size = bdrv_getlength(blkdev->bs); + if (blkdev->file_size < 0) { + xen_be_printf(&blkdev->xendev, 1, "bdrv_getlength: %d (%s) | drv %s\n", + (int)blkdev->file_size, strerror(-blkdev->file_size), + blkdev->bs->drv ? blkdev->bs->drv->format_name : "-"); + blkdev->file_size = 0; + } + have_barriers = blkdev->bs->drv && blkdev->bs->drv->bdrv_flush ? 1 : 0; + + xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," + " size %" PRId64 " (%" PRId64 " MB)\n", + blkdev->type, blkdev->fileproto, blkdev->filename, + blkdev->file_size, blkdev->file_size >> 20); + + /* fill info */ + xenstore_write_be_int(&blkdev->xendev, "feature-barrier", have_barriers); + xenstore_write_be_int(&blkdev->xendev, "info", info); + xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); + xenstore_write_be_int(&blkdev->xendev, "sectors", + blkdev->file_size / blkdev->file_blk); + return 0; +} + +static int blk_connect(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref) == -1) + return -1; + if (xenstore_read_fe_int(&blkdev->xendev, "event-channel", + &blkdev->xendev.remote_port) == -1) + return -1; + + blkdev->protocol = BLKIF_PROTOCOL_NATIVE; + if (blkdev->xendev.protocol) { + if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32) == 0) + blkdev->protocol = BLKIF_PROTOCOL_X86_32; + if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64) == 0) + blkdev->protocol = BLKIF_PROTOCOL_X86_64; + } + + blkdev->sring = xc_gnttab_map_grant_ref(blkdev->xendev.gnttabdev, + blkdev->xendev.dom, + blkdev->ring_ref, + PROT_READ | PROT_WRITE); + if (!blkdev->sring) + return -1; + blkdev->cnt_map++; + + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + { + blkif_sring_t *sring_native = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.native, sring_native, XC_PAGE_SIZE); + break; + } + case BLKIF_PROTOCOL_X86_32: + { + blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.x86_32, sring_x86_32, XC_PAGE_SIZE); + break; + } + case BLKIF_PROTOCOL_X86_64: + { + blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.x86_64, sring_x86_64, XC_PAGE_SIZE); + break; + } + } + + xen_be_bind_evtchn(&blkdev->xendev); + + xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, " + "remote port %d, local port %d\n", + blkdev->xendev.protocol, blkdev->ring_ref, + blkdev->xendev.remote_port, blkdev->xendev.local_port); + return 0; +} + +static void blk_disconnect(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + if (blkdev->bs) { + if (blkdev->index == -1) { + /* close/delete only if we created it ourself */ + bdrv_close(blkdev->bs); + bdrv_delete(blkdev->bs); + } + blkdev->bs = NULL; + } + xen_be_unbind_evtchn(&blkdev->xendev); + + if (blkdev->sring) { + xc_gnttab_munmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); + blkdev->cnt_map--; + blkdev->sring = NULL; + } +} + +static int blk_free(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + struct ioreq *ioreq; + + while (!LIST_EMPTY(&blkdev->freelist)) { + ioreq = LIST_FIRST(&blkdev->freelist); + LIST_REMOVE(ioreq, list); + qemu_iovec_destroy(&ioreq->v); + qemu_free(ioreq); + } + + qemu_free(blkdev->params); + qemu_free(blkdev->mode); + qemu_bh_delete(blkdev->bh); + return 0; +} + +static void blk_event(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + qemu_bh_schedule(blkdev->bh); +} + +struct XenDevOps xen_blkdev_ops = { + .size = sizeof(struct XenBlkDev), + .flags = DEVOPS_FLAG_NEED_GNTDEV, + .alloc = blk_alloc, + .init = blk_init, + .connect = blk_connect, + .disconnect = blk_disconnect, + .event = blk_event, + .free = blk_free, +}; diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 24ec920..e52d3c1 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -59,6 +59,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, xen_be_register("console", &xen_console_ops); xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); + xen_be_register("qdisk", &xen_blkdev_ops); /* setup framebuffer */ xen_init_display(xen_domid); diff --git a/sysemu.h b/sysemu.h index 3eab34b..7b356b3 100644 --- a/sysemu.h +++ b/sysemu.h @@ -127,7 +127,7 @@ extern unsigned int nb_prom_envs; #endif typedef enum { - IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO + IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN } BlockInterfaceType; typedef enum { diff --git a/vl.c b/vl.c index 889aaf6..9d0254d 100644 --- a/vl.c +++ b/vl.c @@ -2368,7 +2368,10 @@ int drive_init(struct drive_opt *arg, int snapshot, void *opaque) } else if (!strcmp(buf, "virtio")) { type = IF_VIRTIO; max_devs = 0; - } else { + } else if (!strcmp(buf, "xen")) { + type = IF_XEN; + max_devs = 0; + } else { fprintf(stderr, "qemu: ''%s'' unsupported bus type ''%s''\n", str, buf); return -1; } @@ -2582,6 +2585,7 @@ int drive_init(struct drive_opt *arg, int snapshot, void *opaque) switch(type) { case IF_IDE: case IF_SCSI: + case IF_XEN: switch(media) { case MEDIA_DISK: if (cyls != 0) { -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-07 14:44 UTC
[Xen-devel] [PATCH 06/10] xen: add net backend driver.
This patch adds a network interface backend driver to qemu. It is a pure userspace implemention using the gntdev interface. It uses "qnet" as backend name in xenstore so it doesn''t interfere with the netback backend (aka "vnif"). The network backend is hooked into the corrosponding qemu vlan, i.e. vif 0 is hooked into vlan 0. To make the packages actually arrive somewhere you additionally have to link the vlan to the outside world using the usual qemu command line options such as "-net tap,...". Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen_backend.h | 1 + hw/xen_machine_pv.c | 1 + hw/xen_nic.c | 396 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 399 insertions(+), 1 deletions(-) create mode 100644 hw/xen_nic.c diff --git a/Makefile.target b/Makefile.target index edbc928..20aff61 100644 --- a/Makefile.target +++ b/Makefile.target @@ -559,7 +559,7 @@ endif # xen backend driver support XEN_OBJS := xen_machine_pv.o xen_backend.o -XEN_OBJS += xen_console.o xenfb.o xen_disk.o +XEN_OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.h b/hw/xen_backend.h index dd426dd..4e4be14 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -89,6 +89,7 @@ extern struct XenDevOps xen_console_ops; /* xen_console.c */ extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ extern struct XenDevOps xen_blkdev_ops; /* xen_disk.c */ +extern struct XenDevOps xen_netdev_ops; /* xen_nic.c */ void xen_init_display(int domid); diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index e52d3c1..09d2667 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -60,6 +60,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); xen_be_register("qdisk", &xen_blkdev_ops); + xen_be_register("qnic", &xen_netdev_ops); /* setup framebuffer */ xen_init_display(xen_domid); diff --git a/hw/xen_nic.c b/hw/xen_nic.c new file mode 100644 index 0000000..bf7dd5f --- /dev/null +++ b/hw/xen_nic.c @@ -0,0 +1,396 @@ +/* + * xen paravirt network card backend + * + * (c) Gerd Hoffmann <kraxel@redhat.com> + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <inttypes.h> +#include <fcntl.h> +#include <errno.h> +#include <pthread.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/wait.h> +#include <linux/if.h> +#include <linux/if_tun.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> +#include <xen/io/netif.h> + +#include "hw.h" +#include "net.h" +#include "qemu-char.h" +#include "xen_backend.h" + +/* ------------------------------------------------------------- */ + +struct XenNetDev { + struct XenDevice xendev; /* must be first */ + char *mac; + int tx_work; + int tx_ring_ref; + int rx_ring_ref; + struct netif_tx_sring *txs; + struct netif_rx_sring *rxs; + netif_tx_back_ring_t tx_ring; + netif_rx_back_ring_t rx_ring; + VLANClientState *vs; +}; + +/* ------------------------------------------------------------- */ + +static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, int8_t st) +{ + RING_IDX i = netdev->tx_ring.rsp_prod_pvt; + netif_tx_response_t *resp; + int notify; + + resp = RING_GET_RESPONSE(&netdev->tx_ring, i); + resp->id = txp->id; + resp->status = st; + +#if 0 + if (txp->flags & NETTXF_extra_info) + RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL; +#endif + + netdev->tx_ring.rsp_prod_pvt = ++i; + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify); + if (notify) + xen_be_send_notify(&netdev->xendev); + + if (i == netdev->tx_ring.req_cons) { + int more_to_do; + RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do); + if (more_to_do) + netdev->tx_work++; + } +} + +static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING_IDX end) +{ +#if 0 + /* + * Hmm, why netback fails everything in the ring? + * Should we do that even when not supporting SG and TSO? + */ + RING_IDX cons = netdev->tx_ring.req_cons; + + do { + make_tx_response(netif, txp, NETIF_RSP_ERROR); + if (cons >= end) + break; + txp = RING_GET_REQUEST(&netdev->tx_ring, cons++); + } while (1); + netdev->tx_ring.req_cons = cons; + netif_schedule_work(netif); + netif_put(netif); +#else + net_tx_response(netdev, txp, NETIF_RSP_ERROR); +#endif +} + +static void net_tx_packets(struct XenNetDev *netdev) +{ + netif_tx_request_t txreq; + RING_IDX rc, rp; + void *page; + void *tmpbuf = NULL; + + for (;;) { + rc = netdev->tx_ring.req_cons; + rp = netdev->tx_ring.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + while ((rc != rp)) { + if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) + break; + memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq)); + netdev->tx_ring.req_cons = ++rc; + +#if 1 + /* should not happen in theory, we don''t announce the * + * feature-{sg,gso,whatelse} flags in xenstore (yet?) */ + if (txreq.flags & NETTXF_extra_info) { + xen_be_printf(&netdev->xendev, 0, "FIXME: extra info flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_more_data) { + xen_be_printf(&netdev->xendev, 0, "FIXME: more data flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } +#endif + + if (txreq.size < 14) { + xen_be_printf(&netdev->xendev, 0, "bad packet size: %d\n", txreq.size); + net_tx_error(netdev, &txreq, rc); + continue; + } + + if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) { + xen_be_printf(&netdev->xendev, 0, "error: page crossing\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + + xen_be_printf(&netdev->xendev, 3, "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n", + txreq.gref, txreq.offset, txreq.size, txreq.flags, + (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", + (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", + (txreq.flags & NETTXF_more_data) ? " more_data" : "", + (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); + + page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + txreq.gref, PROT_READ); + if (page == NULL) { + xen_be_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n", + txreq.gref); + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_csum_blank) { + /* have read-only mapping -> can''t fill checksum in-place */ + if (!tmpbuf) + tmpbuf = malloc(PAGE_SIZE); + memcpy(tmpbuf, page + txreq.offset, txreq.size); + net_checksum_calculate(tmpbuf, txreq.size); + qemu_send_packet(netdev->vs, tmpbuf, txreq.size); + } else { + qemu_send_packet(netdev->vs, page + txreq.offset, txreq.size); + } + xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); + } + if (!netdev->tx_work) + break; + netdev->tx_work = 0; + } + free(tmpbuf); +} + +/* ------------------------------------------------------------- */ + +static void net_rx_response(struct XenNetDev *netdev, + netif_rx_request_t *req, int8_t st, + uint16_t offset, uint16_t size, + uint16_t flags) +{ + RING_IDX i = netdev->rx_ring.rsp_prod_pvt; + netif_rx_response_t *resp; + int notify; + + resp = RING_GET_RESPONSE(&netdev->rx_ring, i); + resp->offset = offset; + resp->flags = flags; + resp->id = req->id; + resp->status = (int16_t)size; + if (st < 0) + resp->status = (int16_t)st; + + xen_be_printf(&netdev->xendev, 3, "rx response: idx %d, status %d, flags 0x%x\n", + i, resp->status, resp->flags); + + netdev->rx_ring.rsp_prod_pvt = ++i; + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify); + if (notify) + xen_be_send_notify(&netdev->xendev); +} + +#define NET_IP_ALIGN 2 + +static int net_rx_ok(void *opaque) +{ + struct XenNetDev *netdev = opaque; + RING_IDX rc, rp; + + if (netdev->xendev.be_state != XenbusStateConnected) + return 0; + + rc = netdev->rx_ring.req_cons; + rp = netdev->rx_ring.sring->req_prod; + xen_rmb(); + + if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { + xen_be_printf(&netdev->xendev, 2, "%s: no rx buffers (%d/%d)\n", + __FUNCTION__, rc, rp); + return 0; + } + return 1; +} + +static void net_rx_packet(void *opaque, const uint8_t *buf, int size) +{ + struct XenNetDev *netdev = opaque; + netif_rx_request_t rxreq; + RING_IDX rc, rp; + void *page; + + if (netdev->xendev.be_state != XenbusStateConnected) + return; + + rc = netdev->rx_ring.req_cons; + rp = netdev->rx_ring.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { + xen_be_printf(&netdev->xendev, 2, "no buffer, drop packet\n"); + return; + } + if (size > XC_PAGE_SIZE - NET_IP_ALIGN) { + xen_be_printf(&netdev->xendev, 0, "packet too big (%d > %ld)", + size, XC_PAGE_SIZE - NET_IP_ALIGN); + return; + } + + memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); + netdev->rx_ring.req_cons = ++rc; + + page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + rxreq.gref, PROT_WRITE); + if (page == NULL) { + xen_be_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n", + rxreq.gref); + net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0); + return; + } + memcpy(page + NET_IP_ALIGN, buf, size); + xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0); +} + +/* ------------------------------------------------------------- */ + +static int net_init(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + VLANState *vlan; + + /* read xenstore entries */ + if (netdev->mac == NULL) + netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac"); + + /* do we have all we need? */ + if (netdev->mac == NULL) + return -1; + + vlan = qemu_find_vlan(netdev->xendev.dev); + netdev->vs = qemu_new_vlan_client(vlan, "xen", NULL, + net_rx_packet, net_rx_ok, netdev); + snprintf(netdev->vs->info_str, sizeof(netdev->vs->info_str), + "nic: xenbus vif macaddr=%s", netdev->mac); + + /* fill info */ + xenstore_write_be_int(&netdev->xendev, "feature-rx-copy", 1); + xenstore_write_be_int(&netdev->xendev, "feature-rx-flip", 0); + + return 0; +} + +static int net_connect(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + int rx_copy; + + if (xenstore_read_fe_int(&netdev->xendev, "tx-ring-ref", + &netdev->tx_ring_ref) == -1) + return -1; + if (xenstore_read_fe_int(&netdev->xendev, "rx-ring-ref", + &netdev->rx_ring_ref) == -1) + return 1; + if (xenstore_read_fe_int(&netdev->xendev, "event-channel", + &netdev->xendev.remote_port) == -1) + return -1; + + if (xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy) == -1) + rx_copy = 0; + if (rx_copy == 0) { + xen_be_printf(&netdev->xendev, 0, "frontend doesn''t support rx-copy.\n"); + return -1; + } + + netdev->txs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + netdev->tx_ring_ref, + PROT_READ | PROT_WRITE); + netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + netdev->rx_ring_ref, + PROT_READ | PROT_WRITE); + if (!netdev->txs || !netdev->rxs) + return -1; + BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE); + BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XC_PAGE_SIZE); + + xen_be_bind_evtchn(&netdev->xendev); + + xen_be_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, " + "remote port %d, local port %d\n", + netdev->tx_ring_ref, netdev->rx_ring_ref, + netdev->xendev.remote_port, netdev->xendev.local_port); + return 0; +} + +static void net_disconnect(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + + xen_be_unbind_evtchn(&netdev->xendev); + + if (netdev->txs) { + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); + netdev->txs = NULL; + } + if (netdev->rxs) { + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1); + netdev->rxs = NULL; + } + if (netdev->vs) { + qemu_del_vlan_client(netdev->vs); + netdev->vs = NULL; + } +} + +static void net_event(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + net_tx_packets(netdev); +} + +/* ------------------------------------------------------------- */ + +struct XenDevOps xen_netdev_ops = { + .size = sizeof(struct XenNetDev), + .flags = DEVOPS_FLAG_NEED_GNTDEV, + .init = net_init, + .connect = net_connect, + .event = net_event, + .disconnect = net_disconnect, +}; -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-07 14:44 UTC
[Xen-devel] [PATCH 07/10] xen: blk & nic configuration via cmd line.
This patch makes qemu create backend and frontend device entries in xenstore for devices configured on the command line. It will use qdisk and qnic backend names, so the qemu internal backends will be used. Disks can be created using -drive if=xen,file=... Nics can be created using -net nic,macaddr=... Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen_backend.c | 1 + hw/xen_backend.h | 8 +++ hw/xen_devconfig.c | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_machine_pv.c | 19 +++++++ 5 files changed, 174 insertions(+), 1 deletions(-) create mode 100644 hw/xen_devconfig.c diff --git a/Makefile.target b/Makefile.target index 20aff61..406b5f7 100644 --- a/Makefile.target +++ b/Makefile.target @@ -558,7 +558,7 @@ LIBS += $(CONFIG_BLUEZ_LIBS) endif # xen backend driver support -XEN_OBJS := xen_machine_pv.o xen_backend.o +XEN_OBJS := xen_machine_pv.o xen_backend.o xen_devconfig.o XEN_OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) diff --git a/hw/xen_backend.c b/hw/xen_backend.c index b13c3f9..d5e4642 100644 --- a/hw/xen_backend.c +++ b/hw/xen_backend.c @@ -45,6 +45,7 @@ /* public */ int xen_xc; struct xs_handle *xenstore = NULL; +const char *xen_protocol; /* private */ static TAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = TAILQ_HEAD_INITIALIZER(xendevs); diff --git a/hw/xen_backend.h b/hw/xen_backend.h index 4e4be14..7f1804e 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -3,6 +3,8 @@ #include "xen_common.h" #include "sysemu.h" +#include "net.h" +#include "block_int.h" /* ------------------------------------------------------------- */ @@ -56,6 +58,7 @@ struct XenDevice { /* variables */ extern int xen_xc; extern struct xs_handle *xenstore; +extern const char *xen_protocol; /* xenstore helper functions */ int xenstore_write_str(const char *base, const char *node, const char *val); @@ -93,4 +96,9 @@ extern struct XenDevOps xen_netdev_ops; /* xen_nic.c */ void xen_init_display(int domid); +/* configuration (aka xenbus setup) */ +void xen_config_cleanup(void); +int xen_config_dev_blk(DriveInfo *disk); +int xen_config_dev_nic(NICInfo *nic); + #endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c new file mode 100644 index 0000000..0ef8012 --- /dev/null +++ b/hw/xen_devconfig.c @@ -0,0 +1,145 @@ +#include "xen_backend.h" + +/* ------------------------------------------------------------- */ + +struct xs_dirs { + char *xs_dir; + TAILQ_ENTRY(xs_dirs) list; +}; +static TAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup = TAILQ_HEAD_INITIALIZER(xs_cleanup); + +static void xen_config_cleanup_dir(char *dir) +{ + struct xs_dirs *d; + + d = qemu_malloc(sizeof(*d)); + if (!d) + return; + d->xs_dir = dir; + TAILQ_INSERT_TAIL(&xs_cleanup, d, list); +} + +void xen_config_cleanup(void) +{ + struct xs_dirs *d; + + fprintf(stderr, "xen be: %s\n", __FUNCTION__); + TAILQ_FOREACH(d, &xs_cleanup, list) { + xs_rm(xenstore, 0, d->xs_dir); + } +} + +/* ------------------------------------------------------------- */ + +static int xen_config_dev_mkdir(char *dev, int p) +{ + struct xs_permissions perms[2] = {{ + .id = 0, /* set owner: dom0 */ + },{ + .id = xen_domid, + .perms = p, + }}; + + if (!xs_mkdir(xenstore, 0, dev)) { + fprintf(stderr, "xs_mkdir %s: failed\n", dev); + return -1; + } + xen_config_cleanup_dir(qemu_strdup(dev)); + + if (!xs_set_permissions(xenstore, 0, dev, perms, 2)) { + fprintf(stderr, "%s: xs_set_permissions failed\n", __FUNCTION__); + return -1; + } + return 0; +} + +static int xen_config_dev_dirs(const char *ftype, const char *btype, int vdev, + char *fe, char *be, int len) +{ + char *dom; + + dom = xs_get_domain_path(xenstore, xen_domid); + snprintf(fe, len, "%s/device/%s/%d", dom, ftype, vdev); + free(dom); + + dom = xs_get_domain_path(xenstore, 0); + snprintf(be, len, "%s/backend/%s/%d/%d", dom, btype, xen_domid, vdev); + free(dom); + + xen_config_dev_mkdir(fe, XS_PERM_READ | XS_PERM_WRITE); + xen_config_dev_mkdir(be, XS_PERM_READ); + return 0; +} + +static int xen_config_dev_all(char *fe, char *be) +{ + /* frontend */ + if (xen_protocol) + xenstore_write_str(fe, "protocol", xen_protocol); + + xenstore_write_int(fe, "state", XenbusStateInitialising); + xenstore_write_int(fe, "backend-id", 0); + xenstore_write_str(fe, "backend", be); + + /* backend */ + xenstore_write_str(be, "domain", qemu_name ? qemu_name : "no-name"); + xenstore_write_int(be, "online", 1); + xenstore_write_int(be, "state", XenbusStateInitialising); + xenstore_write_int(be, "frontend-id", xen_domid); + xenstore_write_str(be, "frontend", fe); + + return 0; +} + +/* ------------------------------------------------------------- */ + +int xen_config_dev_blk(DriveInfo *disk) +{ + char fe[256], be[256]; + int vdev = 202 * 256 + 16 * disk->unit; + int cdrom = disk->bdrv->type == BDRV_TYPE_CDROM; + const char *devtype = cdrom ? "cdrom" : "disk"; + const char *mode = cdrom ? "r" : "w"; + + snprintf(disk->bdrv->device_name, sizeof(disk->bdrv->device_name), + "xvd%c", ''a'' + disk->unit); + fprintf(stderr, "xen be: config disk %d [%s]: %s\n", + disk->unit, disk->bdrv->device_name, disk->bdrv->filename); + xen_config_dev_dirs("vbd", "qdisk", vdev, fe, be, sizeof(fe)); + + /* frontend */ + xenstore_write_int(fe, "virtual-device", vdev); + xenstore_write_str(fe, "device-type", devtype); + + /* backend */ + xenstore_write_str(be, "dev", disk->bdrv->device_name); + xenstore_write_str(be, "type", "file"); + xenstore_write_str(be, "params", disk->bdrv->filename); + xenstore_write_str(be, "mode", mode); + + /* common stuff */ + return xen_config_dev_all(fe, be); +} + +int xen_config_dev_nic(NICInfo *nic) +{ + char fe[256], be[256]; + char mac[20]; + + snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", + nic->macaddr[0], nic->macaddr[1], nic->macaddr[2], + nic->macaddr[3], nic->macaddr[4], nic->macaddr[5]); + fprintf(stderr, "xen be: config nic %d: mac=\"%s\"\n", nic->vlan->id, mac); + xen_config_dev_dirs("vif", "qnic", nic->vlan->id, fe, be, sizeof(fe)); + + /* frontend */ + xenstore_write_int(fe, "handle", nic->vlan->id); + xenstore_write_str(fe, "mac", mac); + + /* backend */ + xenstore_write_int(be, "handle", nic->vlan->id); + xenstore_write_str(be, "mac", mac); + + /* common stuff */ + return xen_config_dev_all(fe, be); +} diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 09d2667..01f9f46 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -39,6 +39,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, const char *cpu_model) { CPUState *env; + int i, index; /* Initialize a dummy CPU */ if (cpu_model == NULL) { @@ -62,6 +63,24 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, xen_be_register("qdisk", &xen_blkdev_ops); xen_be_register("qnic", &xen_netdev_ops); + /* configure disks */ + for (i = 0; i < 16; i++) { + index = drive_get_index(IF_XEN, 0, i); + if (index == -1) + continue; + xen_config_dev_blk(drives_table + index); + } + + /* configure nics */ + for (i = 0; i < nb_nics; i++) { + if (!nd_table[i].model || 0 != strcmp(nd_table[i].model, "xen")) + continue; + xen_config_dev_nic(nd_table + i); + } + + /* config cleanup hook */ + atexit(xen_config_cleanup); + /* setup framebuffer */ xen_init_display(xen_domid); } -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
This adds domain building support for paravirtual domains to qemu. This allows booting xen guests directly with qemu, without Xend and the management stack. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- configure | 2 +- hw/xen_backend.h | 3 + hw/xen_devconfig.c | 29 +++++ hw/xen_domainbuild.c | 294 ++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_domainbuild.h | 13 +++ hw/xen_machine_pv.c | 19 ++++ 7 files changed, 360 insertions(+), 2 deletions(-) create mode 100644 hw/xen_domainbuild.c create mode 100644 hw/xen_domainbuild.h diff --git a/Makefile.target b/Makefile.target index 406b5f7..70d033a 100644 --- a/Makefile.target +++ b/Makefile.target @@ -558,7 +558,7 @@ LIBS += $(CONFIG_BLUEZ_LIBS) endif # xen backend driver support -XEN_OBJS := xen_machine_pv.o xen_backend.o xen_devconfig.o +XEN_OBJS := xen_machine_pv.o xen_backend.o xen_devconfig.o xen_domainbuild.o XEN_OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) diff --git a/configure b/configure index 252d90c..73e86da 100755 --- a/configure +++ b/configure @@ -1534,7 +1534,7 @@ if test "$bluez" = "yes" ; then echo "#define CONFIG_BLUEZ 1" >> $config_h fi if test "$xen" = "yes" ; then - echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak + echo "XEN_LIBS=-lxenstore -lxenctrl -lxenguest" >> $config_mak fi if test "$aio" = "yes" ; then echo "#define CONFIG_AIO 1" >> $config_h diff --git a/hw/xen_backend.h b/hw/xen_backend.h index 7f1804e..4dbfdb4 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -100,5 +100,8 @@ void xen_init_display(int domid); void xen_config_cleanup(void); int xen_config_dev_blk(DriveInfo *disk); int xen_config_dev_nic(NICInfo *nic); +int xen_config_dev_vfb(int vdev, const char *type); +int xen_config_dev_vkbd(int vdev); +int xen_config_dev_console(int vdev); #endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c index 0ef8012..1417e71 100644 --- a/hw/xen_devconfig.c +++ b/hw/xen_devconfig.c @@ -143,3 +143,32 @@ int xen_config_dev_nic(NICInfo *nic) /* common stuff */ return xen_config_dev_all(fe, be); } + +int xen_config_dev_vfb(int vdev, const char *type) +{ + char fe[256], be[256]; + + xen_config_dev_dirs("vfb", "vfb", vdev, fe, be, sizeof(fe)); + + /* backend */ + xenstore_write_str(be, "type", type); + + /* common stuff */ + return xen_config_dev_all(fe, be); +} + +int xen_config_dev_vkbd(int vdev) +{ + char fe[256], be[256]; + + xen_config_dev_dirs("vkbd", "vkbd", vdev, fe, be, sizeof(fe)); + return xen_config_dev_all(fe, be); +} + +int xen_config_dev_console(int vdev) +{ + char fe[256], be[256]; + + xen_config_dev_dirs("console", "console", vdev, fe, be, sizeof(fe)); + return xen_config_dev_all(fe, be); +} diff --git a/hw/xen_domainbuild.c b/hw/xen_domainbuild.c new file mode 100644 index 0000000..99dd63a --- /dev/null +++ b/hw/xen_domainbuild.c @@ -0,0 +1,294 @@ +#include <signal.h> +#include "xen_backend.h" +#include "xen_domainbuild.h" +#include "sysemu.h" +#include "qemu-timer.h" + +#include <xenguest.h> + +static int xenstore_domain_mkdir(char *path) +{ + struct xs_permissions perms_ro[] = {{ + .id = 0, /* set owner: dom0 */ + },{ + .id = xen_domid, + .perms = XS_PERM_READ, + }}; + struct xs_permissions perms_rw[] = {{ + .id = 0, /* set owner: dom0 */ + },{ + .id = xen_domid, + .perms = XS_PERM_READ | XS_PERM_WRITE, + }}; + const char *writable[] = { "device", "control", "error", NULL }; + char subpath[256]; + int i; + + if (!xs_mkdir(xenstore, 0, path)) { + fprintf(stderr, "%s: xs_mkdir %s: failed\n", __FUNCTION__, path); + return -1; + } + if (!xs_set_permissions(xenstore, 0, path, perms_ro, 2)) { + fprintf(stderr, "%s: xs.set_permissions failed\n", __FUNCTION__); + return -1; + } + + for (i = 0; writable[i]; i++) { + snprintf(subpath, sizeof(subpath), "%s/%s", path, writable[i]); + if (!xs_mkdir(xenstore, 0, subpath)) { + fprintf(stderr, "%s: xs.mkdir %s: failed\n", __FUNCTION__, subpath); + return -1; + } + if (!xs_set_permissions(xenstore, 0, subpath, perms_rw, 2)) { + fprintf(stderr, "%s: xs.set_permissions failed\n", __FUNCTION__); + return -1; + } + } + return 0; +} + +int xenstore_domain_init1(const char *kernel, const char *ramdisk, + const char *cmdline) +{ + char *dom, uuid_string[42], vm[256], path[256]; + int i; + + snprintf(uuid_string, sizeof(uuid_string), UUID_FMT, + qemu_uuid[0], qemu_uuid[1], qemu_uuid[2], qemu_uuid[3], + qemu_uuid[4], qemu_uuid[5], qemu_uuid[6], qemu_uuid[7], + qemu_uuid[8], qemu_uuid[9], qemu_uuid[10], qemu_uuid[11], + qemu_uuid[12], qemu_uuid[13], qemu_uuid[14], qemu_uuid[15]); + dom = xs_get_domain_path(xenstore, xen_domid); + snprintf(vm, sizeof(vm), "/vm/%s", uuid_string); + + xenstore_domain_mkdir(dom); + + xenstore_write_str(vm, "image/ostype", "linux"); + if (kernel) + xenstore_write_str(vm, "image/kernel", kernel); + if (ramdisk) + xenstore_write_str(vm, "image/ramdisk", ramdisk); + if (cmdline) + xenstore_write_str(vm, "image/cmdline", cmdline); + + /* name + id */ + xenstore_write_str(vm, "name", qemu_name ? qemu_name : "no-name"); + xenstore_write_str(vm, "uuid", uuid_string); + xenstore_write_str(dom, "name", qemu_name ? qemu_name : "no-name"); + xenstore_write_int(dom, "domid", xen_domid); + xenstore_write_str(dom, "vm", vm); + + /* memory */ + xenstore_write_int(dom, "memory/target", ram_size >> 10); // kB + xenstore_write_int(vm, "memory", ram_size >> 20); // MB + xenstore_write_int(vm, "maxmem", ram_size >> 20); // MB + + /* cpus */ + for (i = 0; i < smp_cpus; i++) { + snprintf(path, sizeof(path), "cpu/%d/availability",i); + xenstore_write_str(dom, path, "online"); + } + xenstore_write_int(vm, "vcpu_avail", smp_cpus); + xenstore_write_int(vm, "vcpus", smp_cpus); + + /* vnc password */ + xenstore_write_str(vm, "vncpassword", "" /* FIXME */); + + free(dom); + return 0; +} + +int xenstore_domain_init2(int xenstore_port, int xenstore_mfn, + int console_port, int console_mfn) +{ + char *dom; + + dom = xs_get_domain_path(xenstore, xen_domid); + + /* signal new domain */ + xs_introduce_domain(xenstore, + xen_domid, + xenstore_mfn, + xenstore_port); + + /* xenstore */ + xenstore_write_int(dom, "store/ring-ref", xenstore_mfn); + xenstore_write_int(dom, "store/port", xenstore_port); + + /* console */ + xenstore_write_str(dom, "console/type", "ioemu"); + xenstore_write_int(dom, "console/limit", 128 * 1024); + xenstore_write_int(dom, "console/ring-ref", console_mfn); + xenstore_write_int(dom, "console/port", console_port); + xen_config_dev_console(0); + + free(dom); + return 0; +} + +/* ------------------------------------------------------------- */ + +static QEMUTimer *xen_poll; + +/* check domain state once per second */ +static void xen_domain_poll(void *opaque) +{ + struct xc_dominfo info; + int rc; + + rc = xc_domain_getinfo(xen_xc, xen_domid, 1, &info); + if ((1 != rc) || (info.domid != xen_domid)) { + fprintf(stderr, "xen: domain %d is gone\n", xen_domid); + goto quit; + } + if (info.dying) { + fprintf(stderr, "xen: domain %d is dying (%s%s)\n", xen_domid, + info.crashed ? "crashed" : "", + info.shutdown ? "shutdown" : ""); + goto quit; + } + + qemu_mod_timer(xen_poll, qemu_get_clock(rt_clock) + 1000); + return; + +quit: + qemu_system_shutdown_request(); + return; +} + +static void xen_domain_watcher(void) +{ + int qemu_running = 1; + int fd[2], i, n, rc; + char byte; + + pipe(fd); + if (0 != fork()) + return; /* not child */ + + /* close all file handles, except stdio/out/err, + * our watch pipe and the xen interface handle */ + n = getdtablesize(); + for (i = 3; i < n; i++) { + if (i == fd[0]) + continue; + if (i == xen_xc) + continue; + close(i); + } + + /* ignore term signals */ + signal(SIGINT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + + /* wait for qemu exiting */ + while (qemu_running) { + rc = read(fd[0], &byte, 1); + switch (rc) { + case -1: + if (EINTR == errno) + continue; + fprintf(stderr, "%s: Huh? read error: %s\n", __FUNCTION__, strerror(errno)); + qemu_running = 0; + break; + case 0: + /* EOF -> qemu exited */ + qemu_running = 0; + break; + default: + fprintf(stderr, "%s: Huh? data on the watch pipe?\n", __FUNCTION__); + break; + } + } + + /* cleanup */ + fprintf(stderr, "%s: destroy domain %d\n", __FUNCTION__, xen_domid); + xc_domain_destroy(xen_xc, xen_domid); + _exit(0); +} + +/* normal cleanup */ +static void xen_domain_cleanup(void) +{ + char *dom; + + dom = xs_get_domain_path(xenstore, xen_domid); + if (dom) { + xs_rm(xenstore, 0, dom); + free(dom); + } + xs_release_domain(xenstore, xen_domid); +} + +int xen_domain_build_pv(const char *kernel, const char *ramdisk, + const char *cmdline) +{ + uint32_t ssidref = 0; + uint32_t flags = 0; + xen_domain_handle_t uuid; + unsigned int xenstore_port = 0, console_port = 0; + unsigned long xenstore_mfn = 0, console_mfn = 0; + int rc; + + memcpy(uuid, qemu_uuid, sizeof(uuid)); + rc = xc_domain_create(xen_xc, ssidref, uuid, flags, &xen_domid); + if (rc < 0) { + fprintf(stderr, "xen: xc_domain_create() failed\n"); + goto err; + } + fprintf(stderr, "xen: created domain %d\n", xen_domid); + atexit(xen_domain_cleanup); + xen_domain_watcher(); + + xenstore_domain_init1(kernel, ramdisk, cmdline); + + rc = xc_domain_max_vcpus(xen_xc, xen_domid, smp_cpus); + if (rc < 0) { + fprintf(stderr, "xen: xc_domain_max_vcpus() failed\n"); + goto err; + } + +#if 0 + rc = xc_domain_setcpuweight(xen_xc, xen_domid, 256); + if (rc < 0) { + fprintf(stderr, "xen: xc_domain_setcpuweight() failed\n"); + goto err; + } +#endif + + rc = xc_domain_setmaxmem(xen_xc, xen_domid, ram_size >> 10); + if (rc < 0) { + fprintf(stderr, "xen: xc_domain_setmaxmem() failed\n"); + goto err; + } + + xenstore_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); + console_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); + + rc = xc_linux_build(xen_xc, xen_domid, ram_size >> 20, + kernel, ramdisk, cmdline, + 0, flags, + xenstore_port, &xenstore_mfn, + console_port, &console_mfn); + if (rc < 0) { + fprintf(stderr, "xen: xc_linux_build() failed\n"); + goto err; + } + + xenstore_domain_init2(xenstore_port, xenstore_mfn, + console_port, console_mfn); + + fprintf(stderr, "xen: unpausing domain %d\n", xen_domid); + rc = xc_domain_unpause(xen_xc, xen_domid); + if (rc < 0) { + fprintf(stderr, "xen: xc_domain_unpause() failed\n"); + goto err; + } + + xen_poll = qemu_new_timer(rt_clock, xen_domain_poll, NULL); + qemu_mod_timer(xen_poll, qemu_get_clock(rt_clock) + 1000); + return 0; + +err: + return -1; +} diff --git a/hw/xen_domainbuild.h b/hw/xen_domainbuild.h new file mode 100644 index 0000000..dea0121 --- /dev/null +++ b/hw/xen_domainbuild.h @@ -0,0 +1,13 @@ +#ifndef QEMU_HW_XEN_DOMAINBUILD_H +#define QEMU_HW_XEN_DOMAINBUILD_H 1 + +#include "xen_common.h" + +int xenstore_domain_init1(const char *kernel, const char *ramdisk, + const char *cmdline); +int xenstore_domain_init2(int xenstore_port, int xenstore_mfn, + int console_port, int console_mfn); +int xen_domain_build_pv(const char *kernel, const char *ramdisk, + const char *cmdline); + +#endif /* QEMU_HW_XEN_DOMAINBUILD_H */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 01f9f46..4e8b88a 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -27,6 +27,7 @@ #include "sysemu.h" #include "boards.h" #include "xen_backend.h" +#include "xen_domainbuild.h" uint32_t xen_domid; enum xen_mode xen_mode = XEN_EMULATE; @@ -57,6 +58,24 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); exit(1); } + + switch (xen_mode) { + case XEN_ATTACH: + /* nothing to do, xend handles everything */ + break; + case XEN_CREATE: + if (xen_domain_build_pv(kernel_filename, initrd_filename, + kernel_cmdline) < 0) { + fprintf(stderr, "xen pv domain creation failed\n"); + exit(1); + } + break; + case XEN_EMULATE: + fprintf(stderr, "xen emulation not implemented (yet)\n"); + exit(1); + break; + } + xen_be_register("console", &xen_console_ops); xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- vl.c | 15 ++++----------- 1 files changed, 4 insertions(+), 11 deletions(-) diff --git a/vl.c b/vl.c index 9d0254d..724f7ef 100644 --- a/vl.c +++ b/vl.c @@ -4153,23 +4153,16 @@ static void select_vgahw (const char *p) { const char *opts; + cirrus_vga_enabled = 0; + std_vga_enabled = 0; + vmsvga_enabled = 0; if (strstart(p, "std", &opts)) { std_vga_enabled = 1; - cirrus_vga_enabled = 0; - vmsvga_enabled = 0; } else if (strstart(p, "cirrus", &opts)) { cirrus_vga_enabled = 1; - std_vga_enabled = 0; - vmsvga_enabled = 0; } else if (strstart(p, "vmware", &opts)) { - cirrus_vga_enabled = 0; - std_vga_enabled = 0; vmsvga_enabled = 1; - } else if (strstart(p, "none", &opts)) { - cirrus_vga_enabled = 0; - std_vga_enabled = 0; - vmsvga_enabled = 0; - } else { + } else if (!strstart(p, "none", &opts)) { invalid_vga: fprintf(stderr, "Unknown vga type: %s\n", p); exit(1); -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-07 14:44 UTC
[Xen-devel] [PATCH 10/10] xen: add -vga xenfb option, configure xenfb
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- hw/xen_machine_pv.c | 6 ++++++ qemu-options.hx | 2 +- sysemu.h | 1 + vl.c | 4 ++++ 4 files changed, 12 insertions(+), 1 deletions(-) diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 4e8b88a..028a8af 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -82,6 +82,12 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, xen_be_register("qdisk", &xen_blkdev_ops); xen_be_register("qnic", &xen_netdev_ops); + /* configure framebuffer */ + if (xenfb_enabled) { + xen_config_dev_vfb(0, "vnc"); + xen_config_dev_vkbd(0); + } + /* configure disks */ for (i = 0; i < 16; i++) { index = drive_get_index(IF_XEN, 0, i); diff --git a/qemu-options.hx b/qemu-options.hx index 9fc0625..309b931 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -450,7 +450,7 @@ Rotate graphical output 90 deg left (only PXA LCD). ETEXI DEF("vga", HAS_ARG, QEMU_OPTION_vga, - "-vga [std|cirrus|vmware|none]\n" + "-vga [std|cirrus|vmware|xenfb|none]\n" " select video card type\n") STEXI @item -vga @var{type} diff --git a/sysemu.h b/sysemu.h index 7b356b3..e22612b 100644 --- a/sysemu.h +++ b/sysemu.h @@ -87,6 +87,7 @@ extern int bios_size; extern int cirrus_vga_enabled; extern int std_vga_enabled; extern int vmsvga_enabled; +extern int xenfb_enabled; extern int graphic_width; extern int graphic_height; extern int graphic_depth; diff --git a/vl.c b/vl.c index 724f7ef..25810e1 100644 --- a/vl.c +++ b/vl.c @@ -215,6 +215,7 @@ static int rtc_date_offset = -1; /* -1 means no change */ int cirrus_vga_enabled = 1; int std_vga_enabled = 0; int vmsvga_enabled = 0; +int xenfb_enabled = 0; #ifdef TARGET_SPARC int graphic_width = 1024; int graphic_height = 768; @@ -4156,12 +4157,15 @@ static void select_vgahw (const char *p) cirrus_vga_enabled = 0; std_vga_enabled = 0; vmsvga_enabled = 0; + xenfb_enabled = 0; if (strstart(p, "std", &opts)) { std_vga_enabled = 1; } else if (strstart(p, "cirrus", &opts)) { cirrus_vga_enabled = 1; } else if (strstart(p, "vmware", &opts)) { vmsvga_enabled = 1; + } else if (strstart(p, "xenfb", &opts)) { + xenfb_enabled = 1; } else if (!strstart(p, "none", &opts)) { invalid_vga: fprintf(stderr, "Unknown vga type: %s\n", p); -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Christoph Hellwig
2009-Apr-07 15:51 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 05/10] xen: add block device backend driver.
On Tue, Apr 07, 2009 at 12:22:00PM +0200, Gerd Hoffmann wrote:> Hi, > > >We already have bdrv_aio_readv/writev which currently linearize the > >buffer underneath. Hopefully Anthony will have commited the patch to > >implement the real one while I''m writing this, too :) > > Is there any specific reason to keep the nb_sectors argument for > bdrv_aio_readv, other than cut&paste from the non-vectored versions? > I think it is superfluous now, it can be trivially calculated using > iov->size.For the functions in block.c I want to keep them the same as they were before, and for the methods I want to keep the signature the same as the exported wrappers. All this is going to change pretty soon I hope, we''ll need another argument to bdrv_aio_write for barrier writes, and I might switch to a completely byte based interface for the aio requests with flags in the block driver state for flagging the alignment restrictions. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2009-Apr-07 19:41 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 02/10] xen: backend driver core
Gerd Hoffmann wrote:> This patch adds infrastructure for xen backend drivers living in qemu, > so drivers don''t need to implement common stuff on their own. It''s > mostly xenbus management stuff: some functions to access xentore, > setting up xenstore watches, callbacks on device discovery and state > changes, handle event channel, ... > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > --- > Makefile.target | 2 +- > hw/xen_backend.c | 691 +++++++++++++++++++++++++++++++++++++++++++++++++++ > hw/xen_backend.h | 86 +++++++ > hw/xen_common.h | 34 +++ > hw/xen_machine_pv.c | 8 +- > 5 files changed, 819 insertions(+), 2 deletions(-) > create mode 100644 hw/xen_backend.c > create mode 100644 hw/xen_backend.h > create mode 100644 hw/xen_common.h > > diff --git a/Makefile.target b/Makefile.target > index 1cad75e..5d9dfc7 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -558,7 +558,7 @@ LIBS += $(CONFIG_BLUEZ_LIBS) > endif > > # xen backend driver support > -XEN_OBJS := xen_machine_pv.o > +XEN_OBJS := xen_machine_pv.o xen_backend.o > ifeq ($(CONFIG_XEN), yes) > OBJS += $(XEN_OBJS) > LIBS += $(XEN_LIBS) > diff --git a/hw/xen_backend.c b/hw/xen_backend.c > new file mode 100644 > index 0000000..b13c3f9 > --- /dev/null > +++ b/hw/xen_backend.c > @@ -0,0 +1,691 @@ > +/* > + * xen backend driver infrastructure > + * (c) 2008 Gerd Hoffmann <kraxel@redhat.com> > + * > + * 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, write to the Free Software Foundation, Inc., > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > + */ > + > +/* > + * TODO: add some xenbus / xenstore concepts overview here. > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <stdarg.h> > +#include <string.h> > +#include <unistd.h> > +#include <fcntl.h> > +#include <inttypes.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <sys/mman.h> > +#include <sys/signal.h> > + > +#include <xs.h> > +#include <xenctrl.h> > +#include <xen/grant_table.h> > + > +#include "hw.h" > +#include "qemu-char.h" > +#include "xen_backend.h" > + > +/* ------------------------------------------------------------- */ > + > +/* public */ > +int xen_xc; > +struct xs_handle *xenstore = NULL; > + > +/* private */ > +static TAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = TAILQ_HEAD_INITIALIZER(xendevs); > +static int debug = 0; >Would be better to have all of this in a structure that had a single static instance. Would be even better if you could avoid requiring the static instance.> +/* ------------------------------------------------------------- */ > + > +int xenstore_write_str(const char *base, const char *node, const char *val) > +{ > + char abspath[XEN_BUFSIZE]; > + > + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); > + if (!xs_write(xenstore, 0, abspath, val, strlen(val))) > + return -1; > + return 0; > +} > + > +char *xenstore_read_str(const char *base, const char *node) > +{ > + char abspath[XEN_BUFSIZE]; > + unsigned int len; > + > + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); > + return xs_read(xenstore, 0, abspath, &len); >xs_read() is a xenstore API, it''s returning malloc()''d memory.> +} > + > +int xenstore_write_int(const char *base, const char *node, int ival) > +{ > + char val[32]; > + > + snprintf(val, sizeof(val), "%d", ival); > + return xenstore_write_str(base, node, val); > +} > + > +int xenstore_read_int(const char *base, const char *node, int *ival) > +{ > + char *val; > + int rc = -1; > + > + val = xenstore_read_str(base, node); > + if (val && 1 == sscanf(val, "%d", ival)) > + rc = 0; > + qemu_free(val); >And here you''re free()''ing with qemu_free.> +/* > + * get xen backend device, allocate a new one if it doesn''t exist. > + */ > +static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, > + struct XenDevOps *ops) > +{ > + struct XenDevice *xendev; > + char *dom0; > + > + xendev = xen_be_find_xendev(type, dom, dev); > + if (xendev) > + return xendev; > + > + /* init new xendev */ > + xendev = qemu_mallocz(ops->size); > + if (!xendev) > + return NULL; >No need to check malloc failures.> + > + /* look for backends */ > + dev = xs_directory(xenstore, 0, path, &cdev); > + if (!dev) > + return 0; > + for (j = 0; j < cdev; j++) { > + xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops); > + if (xendev == NULL) > + continue; > + xen_be_check_state(xendev); > + } > + qemu_free(dev); >Mixing qemu_free() with malloc''d memory.> +static void xenstore_update_be(char *watch, char *type, int dom, > + struct XenDevOps *ops) > +{ > + struct XenDevice *xendev; > + char path[XEN_BUFSIZE], *dom0; > + unsigned int len, dev; > + > + dom0 = xs_get_domain_path(xenstore, 0); > + len = snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom); > + free(dom0); > + if (0 != strncmp(path, watch, len)) > + return; > + if (2 != sscanf(watch+len, "/%u/%255s", &dev, path)) { > + strcpy(path, ""); > + if (1 != sscanf(watch+len, "/%u", &dev)) > + dev = -1; > + } >Overly defensive ifs. You also have open coded calls to fprintf(stderr) whereas you''ve introduced a higher level function. Regards, Anthony Liguori _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-08 09:21 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 02/10] xen: backend driver core
On 04/07/09 21:41, Anthony Liguori wrote:>> +/* private */ >> +static TAILQ_HEAD(XenDeviceHead, XenDevice) xendevs >> TAILQ_HEAD_INITIALIZER(xendevs); >> +static int debug = 0; > > Would be better to have all of this in a structure that had a single > static instance. Would be even better if you could avoid requiring the > static instance.Huh? Point being? This is just a list head, i.e. a pointer (or two?).>> +char *xenstore_read_str(const char *base, const char *node) >> +{ >> + char abspath[XEN_BUFSIZE]; >> + unsigned int len; >> + >> + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); >> + return xs_read(xenstore, 0, abspath, &len); > > xs_read() is a xenstore API, it''s returning malloc()''d memory.>> +int xenstore_read_int(const char *base, const char *node, int *ival) >> +{ >> + char *val; >> + int rc = -1; >> + >> + val = xenstore_read_str(base, node); >> + if (val && 1 == sscanf(val, "%d", ival)) >> + rc = 0; >> + qemu_free(val); > > And here you''re free()''ing with qemu_free.Oops. Good catch.>> + xendev = qemu_mallocz(ops->size); >> + if (!xendev) >> + return NULL; > > No need to check malloc failures.Will fix.>> + dev = xs_directory(xenstore, 0, path, &cdev); >> + qemu_free(dev); > > Mixing qemu_free() with malloc''d memory.This too.> You also have open coded calls to fprintf(stderr) > whereas you''ve introduced a higher level function.The high-level function wants a device instance and thus doesn''t work everythere. Nevertheless the code probably should use the new qemu_log() function instead. I''ll look into this. cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-08 09:52 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 02/10] xen: backend driver core
On 04/08/09 11:21, Gerd Hoffmann wrote:> Nevertheless the code probably should use the new qemu_log() > function instead.Well, for debug/info only. Errors should still go to stderr I think. cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2009-Apr-08 13:17 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 02/10] xen: backend driver core
Gerd Hoffmann wrote:> On 04/07/09 21:41, Anthony Liguori wrote: >>> +/* private */ >>> +static TAILQ_HEAD(XenDeviceHead, XenDevice) xendevs >>> TAILQ_HEAD_INITIALIZER(xendevs); >>> +static int debug = 0; >> >> Would be better to have all of this in a structure that had a single >> static instance. Would be even better if you could avoid requiring the >> static instance. > > Huh? Point being? This is just a list head, i.e. a pointer (or two?).I meant the public stuff too. I don''t like having extern variables all over the place. It would be better to have this in a structure that was passed to all of the backend drivers that they then passed as a context. This will become more useful down the road too when we start introducing a common device model and finer grain locking. Regards, Anthony Liguori _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-08 14:08 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support.
On 04/07/09 19:30, Blue Swirl wrote:> On 4/7/09, Gerd Hoffmann<kraxel@redhat.com> wrote: >> Hi, >> >> Next round, addressing review comments. Bulk of the changes are just >> coding style, i.e. swap ordering of the funny "if (-1 == foo()) bar()" >> compare style. Also the FSF address is fixed everythere. And the > > There are still a lot of those funny comparisons.Oops. My grep missed the != comparisons. And a single one here and there. cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-08 14:25 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 02/10] xen: backend driver core
On 04/08/09 15:17, Anthony Liguori wrote:> I meant the public stuff too. I don''t like having extern variables all > over the place. > > It would be better to have this in a structure that was passed to all of > the backend drivers that they then passed as a context. This will become > more useful down the road too when we start introducing a common device > model and finer grain locking.The variables are more global xen state, not device / xenbus state. The usage is not limited to the backend drivers, the domain builder code will use them as well. There will never ever be more than one instance of these. I don''t see the point of placing them into a struct and passing around a pointer. cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Hi, Next round, addressing review comments: * Fixup more "if (-1 == foo()) bar()" compare style. * Fixup logging: kill fprintf(stderr, ...), use logging API. * Kill some unneeded malloc failure checks. The patches are also rebased to latest git and got a few adaptions to recent changes. Short overview (individual patches have longer descriptions): #1 - groundwork for xen support (makefiles, configure, ...). #2 - backend driver core (common code used by all backends). #3 - add console backend driver. #4 - add framebuffer backend driver. With these four patches in place upstream qemu is functional aequivalent to qemu-xen for paravirtual guests. The patches are merged into qemu-xen already, with the exception of a few framebuffer bits which got hold back due to displaystate work and the most recent updates due to review comments. #5 - add block device backend driver. #6 - add net backend driver. These two patches add backend drivers for disk and network to qemu. #7 - blk & nic configuration via cmd line. #8 - pv domain builder. #9 - simplify vga selection #10 - add -vga xenfb option, configure xenfb These patches add support to qemu for creating xen pv guests. That way one can run xen guests without xend. Patch #8 is the domain builder code itself. The other patches add support for configuring devices (fb, disk, nic) via command line. The patches are also available via git: git: git://git.et.redhat.com/qemu-kraxel.git gitweb: http://git.et.redhat.com/?p=qemu-kraxel.git branches: xenbits.v7 - this patch series. qx.misc.v6 - xenfb update + review changes for qemu-xen. Ian, is just pulling from git fine with you? Or do you want the qemu-xen patches mailed to xen-devel? cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-21 12:19 UTC
[Xen-devel] [PATCH 01/10] xen: groundwork for xen support
- configure script and build system changes. - wind up new machine type. - add -xen-* command line options. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 7 +++++ configure | 34 +++++++++++++++++++++++++++ hw/boards.h | 3 ++ hw/xen.h | 20 ++++++++++++++++ hw/xen_machine_pv.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ qemu-options.hx | 11 +++++++++ target-i386/machine.c | 3 ++ vl.c | 12 +++++++++ 8 files changed, 150 insertions(+), 0 deletions(-) create mode 100644 hw/xen.h create mode 100644 hw/xen_machine_pv.c diff --git a/Makefile.target b/Makefile.target index b27696a..ab59af3 100644 --- a/Makefile.target +++ b/Makefile.target @@ -560,6 +560,13 @@ ifdef CONFIG_BLUEZ LIBS += $(CONFIG_BLUEZ_LIBS) endif +# xen backend driver support +XEN_OBJS := xen_machine_pv.o +ifeq ($(CONFIG_XEN), yes) + OBJS += $(XEN_OBJS) + LIBS += $(XEN_LIBS) +endif + # SCSI layer OBJS+= lsi53c895a.o esp.o diff --git a/configure b/configure index 8bf44de..ec761ff 100755 --- a/configure +++ b/configure @@ -191,6 +191,7 @@ aix="no" blobs="yes" fdt="yes" sdl_x11="no" +xen="yes" pkgversion="" # OS specific @@ -421,6 +422,8 @@ for opt do ;; --disable-kqemu) kqemu="no" ;; + --disable-xen) xen="no" + ;; --disable-brlapi) brlapi="no" ;; --disable-bluez) bluez="no" @@ -586,6 +589,7 @@ echo " Available drivers: $audio_possible_drivers" echo " --audio-card-list=LIST set list of emulated audio cards [$audio_card_list]" echo " Available cards: $audio_possible_cards" echo " --enable-mixemu enable mixer emulation" +echo " --disable-xen disable xen backend driver support" echo " --disable-brlapi disable BrlAPI" echo " --disable-vnc-tls disable TLS encryption for VNC server" echo " --disable-vnc-sasl disable SASL encryption for VNC server" @@ -802,6 +806,22 @@ else fi ########################################## +# xen probe + +if test "$xen" = "yes" ; then +cat > $TMPC <<EOF +#include <xenctrl.h> +#include <xs.h> +int main(void) { xs_daemon_open; xc_interface_open; } +EOF + if $cc $ARCH_CFLAGS -c -o $TMPO $TMPC -lxenstore -lxenctrl 2> /dev/null ; then + : + else + xen="no" + fi +fi + +########################################## # SDL probe sdl_too_old=no @@ -1296,6 +1316,7 @@ if test -n "$sparc_cpu"; then echo "Target Sparc Arch $sparc_cpu" fi echo "kqemu support $kqemu" +echo "xen support $xen" echo "brlapi support $brlapi" echo "Documentation $build_docs" [ ! -z "$uname_release" ] && \ @@ -1612,6 +1633,9 @@ if test "$bluez" = "yes" ; then echo "CONFIG_BLUEZ_LIBS=$bluez_libs" >> $config_mak echo "#define CONFIG_BLUEZ 1" >> $config_h fi +if test "$xen" = "yes" ; then + echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak +fi if test "$aio" = "yes" ; then echo "#define CONFIG_AIO 1" >> $config_h echo "CONFIG_AIO=yes" >> $config_mak @@ -1777,6 +1801,11 @@ case "$target_cpu" in echo "KVM_CFLAGS=$kvm_cflags" >> $config_mak echo "#define CONFIG_KVM 1" >> $config_h fi + if test "$xen" = "yes" -a "$target_softmmu" = "yes"; + then + echo "CONFIG_XEN=yes" >> $config_mak + echo "#define CONFIG_XEN 1" >> $config_h + fi ;; x86_64) echo "TARGET_ARCH=x86_64" >> $config_mak @@ -1793,6 +1822,11 @@ case "$target_cpu" in echo "KVM_CFLAGS=$kvm_cflags" >> $config_mak echo "#define CONFIG_KVM 1" >> $config_h fi + if test "$xen" = "yes" -a "$target_softmmu" = "yes" + then + echo "CONFIG_XEN=yes" >> $config_mak + echo "#define CONFIG_XEN 1" >> $config_h + fi ;; alpha) echo "TARGET_ARCH=alpha" >> $config_mak diff --git a/hw/boards.h b/hw/boards.h index 7fada94..5611d2c 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -32,6 +32,9 @@ extern QEMUMachine axisdev88_machine; extern QEMUMachine pc_machine; extern QEMUMachine isapc_machine; +/* xen_machine.c */ +extern QEMUMachine xenpv_machine; + /* ppc.c */ extern QEMUMachine prep_machine; extern QEMUMachine core99_machine; diff --git a/hw/xen.h b/hw/xen.h new file mode 100644 index 0000000..3c8da41 --- /dev/null +++ b/hw/xen.h @@ -0,0 +1,20 @@ +#ifndef QEMU_HW_XEN_H +#define QEMU_HW_XEN_H 1 +/* + * public xen header + * stuff needed outside xen-*.c, i.e. interfaces to qemu. + * must not depend on any xen headers being present in + * /usr/include/xen, so it can be included unconditionally. + */ + +/* xen-machine.c */ +enum xen_mode { + XEN_EMULATE = 0, // xen emulation, using xenner (default) + XEN_CREATE, // create xen domain + XEN_ATTACH // attach to xen domain created by xend +}; + +extern uint32_t xen_domid; +extern enum xen_mode xen_mode; + +#endif /* QEMU_HW_XEN_H */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c new file mode 100644 index 0000000..f32ba7f --- /dev/null +++ b/hw/xen_machine_pv.c @@ -0,0 +1,60 @@ +/* + * QEMU Xen PV Machine + * + * Copyright (c) 2007 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw.h" +#include "pc.h" +#include "sysemu.h" +#include "boards.h" +#include "xen.h" + +uint32_t xen_domid; +enum xen_mode xen_mode = XEN_EMULATE; + +static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + CPUState *env; + + /* Initialize a dummy CPU */ + if (cpu_model == NULL) { +#ifdef TARGET_X86_64 + cpu_model = "qemu64"; +#else + cpu_model = "qemu32"; +#endif + } + env = cpu_init(cpu_model); + env->halted = 1; +} + +QEMUMachine xenpv_machine = { + .name = "xenpv", + .desc = "Xen Para-virtualized PC", + .init = xen_init_pv, + .max_cpus = 1, +}; diff --git a/qemu-options.hx b/qemu-options.hx index 1d783e5..1f78021 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1333,6 +1333,17 @@ Enable KVM full virtualization support. This option is only available if KVM support is enabled when compiling. ETEXI +#ifdef CONFIG_XEN +DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid, + "-xen-domid id specify xen guest domain id\n") +DEF("xen-create", 0, QEMU_OPTION_xen_create, + "-xen-create create domain using xen hypercalls, bypassing xend\n" + " warning: should not be used when xend is in use\n") +DEF("xen-attach", 0, QEMU_OPTION_xen_attach, + "-xen-attach attach to existing xen domain\n" + " xend will use this when starting qemu\n") +#endif + DEF("no-reboot", 0, QEMU_OPTION_no_reboot, \ "-no-reboot exit instead of rebooting\n") STEXI diff --git a/target-i386/machine.c b/target-i386/machine.c index 1cf49d5..1b0d36d 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -9,6 +9,9 @@ void register_machines(void) { qemu_register_machine(&pc_machine); qemu_register_machine(&isapc_machine); +#ifdef CONFIG_XEN + qemu_register_machine(&xenpv_machine); +#endif } static void cpu_put_seg(QEMUFile *f, SegmentCache *dt) diff --git a/vl.c b/vl.c index 55a9bc5..b681775 100644 --- a/vl.c +++ b/vl.c @@ -139,6 +139,7 @@ int main(int argc, char **argv) #include "hw/baum.h" #include "hw/bt.h" #include "hw/smbios.h" +#include "hw/xen.h" #include "bt-host.h" #include "net.h" #include "monitor.h" @@ -4972,6 +4973,17 @@ int main(int argc, char **argv, char **envp) run_as = optarg; break; #endif +#ifdef CONFIG_XEN + case QEMU_OPTION_xen_domid: + xen_domid = atoi(optarg); + break; + case QEMU_OPTION_xen_create: + xen_mode = XEN_CREATE; + break; + case QEMU_OPTION_xen_attach: + xen_mode = XEN_ATTACH; + break; +#endif } } } -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
This patch adds infrastructure for xen backend drivers living in qemu, so drivers don''t need to implement common stuff on their own. It''s mostly xenbus management stuff: some functions to access xentore, setting up xenstore watches, callbacks on device discovery and state changes, handle event channel, ... Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen_backend.c | 713 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_backend.h | 86 ++++++ hw/xen_common.h | 34 +++ hw/xen_machine_pv.c | 8 +- 5 files changed, 841 insertions(+), 2 deletions(-) create mode 100644 hw/xen_backend.c create mode 100644 hw/xen_backend.h create mode 100644 hw/xen_common.h diff --git a/Makefile.target b/Makefile.target index ab59af3..52c8cd2 100644 --- a/Makefile.target +++ b/Makefile.target @@ -561,7 +561,7 @@ LIBS += $(CONFIG_BLUEZ_LIBS) endif # xen backend driver support -XEN_OBJS := xen_machine_pv.o +XEN_OBJS := xen_machine_pv.o xen_backend.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.c b/hw/xen_backend.c new file mode 100644 index 0000000..c1e3728 --- /dev/null +++ b/hw/xen_backend.c @@ -0,0 +1,713 @@ +/* + * xen backend driver infrastructure + * (c) 2008 Gerd Hoffmann <kraxel@redhat.com> + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * TODO: add some xenbus / xenstore concepts overview here. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/signal.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/grant_table.h> + +#include "hw.h" +#include "qemu-char.h" +#include "xen_backend.h" + +/* ------------------------------------------------------------- */ + +/* public */ +int xen_xc; +struct xs_handle *xenstore = NULL; + +/* private */ +static TAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = TAILQ_HEAD_INITIALIZER(xendevs); +static int debug = 0; + +/* ------------------------------------------------------------- */ + +int xenstore_write_str(const char *base, const char *node, const char *val) +{ + char abspath[XEN_BUFSIZE]; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + if (!xs_write(xenstore, 0, abspath, val, strlen(val))) + return -1; + return 0; +} + +char *xenstore_read_str(const char *base, const char *node) +{ + char abspath[XEN_BUFSIZE]; + unsigned int len; + char *str, *ret = NULL; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + str = xs_read(xenstore, 0, abspath, &len); + if (str != NULL) { + /* move to qemu-allocated memory to make sure + * callers can savely qemu_free() stuff. */ + ret = qemu_strdup(str); + free(str); + } + return ret; +} + +int xenstore_write_int(const char *base, const char *node, int ival) +{ + char val[32]; + + snprintf(val, sizeof(val), "%d", ival); + return xenstore_write_str(base, node, val); +} + +int xenstore_read_int(const char *base, const char *node, int *ival) +{ + char *val; + int rc = -1; + + val = xenstore_read_str(base, node); + if (val && 1 == sscanf(val, "%d", ival)) + rc = 0; + qemu_free(val); + return rc; +} + +int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val) +{ + return xenstore_write_str(xendev->be, node, val); +} + +int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival) +{ + return xenstore_write_int(xendev->be, node, ival); +} + +char *xenstore_read_be_str(struct XenDevice *xendev, const char *node) +{ + return xenstore_read_str(xendev->be, node); +} + +int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival) +{ + return xenstore_read_int(xendev->be, node, ival); +} + +char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node) +{ + return xenstore_read_str(xendev->fe, node); +} + +int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival) +{ + return xenstore_read_int(xendev->fe, node, ival); +} + +/* ------------------------------------------------------------- */ + +const char *xenbus_strstate(enum xenbus_state state) +{ + static const char *const name[] = { + [ XenbusStateUnknown ] = "Unknown", + [ XenbusStateInitialising ] = "Initialising", + [ XenbusStateInitWait ] = "InitWait", + [ XenbusStateInitialised ] = "Initialised", + [ XenbusStateConnected ] = "Connected", + [ XenbusStateClosing ] = "Closing", + [ XenbusStateClosed ] = "Closed", + }; + return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; +} + +int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) +{ + int rc; + + rc = xenstore_write_be_int(xendev, "state", state); + if (rc < 0) + return rc; + xen_be_printf(xendev, 1, "backend state: %s -> %s\n", + xenbus_strstate(xendev->be_state), xenbus_strstate(state)); + xendev->be_state = state; + return 0; +} + +/* ------------------------------------------------------------- */ + +struct XenDevice *xen_be_find_xendev(const char *type, int dom, int dev) +{ + struct XenDevice *xendev; + + TAILQ_FOREACH(xendev, &xendevs, next) { + if (xendev->dom != dom) + continue; + if (xendev->dev != dev) + continue; + if (strcmp(xendev->type, type) != 0) + continue; + return xendev; + } + return NULL; +} + +/* + * get xen backend device, allocate a new one if it doesn''t exist. + */ +static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, + struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char *dom0; + + xendev = xen_be_find_xendev(type, dom, dev); + if (xendev) + return xendev; + + /* init new xendev */ + xendev = qemu_mallocz(ops->size); + xendev->type = type; + xendev->dom = dom; + xendev->dev = dev; + xendev->ops = ops; + + dom0 = xs_get_domain_path(xenstore, 0); + snprintf(xendev->be, sizeof(xendev->be), "%s/backend/%s/%d/%d", + dom0, xendev->type, xendev->dom, xendev->dev); + snprintf(xendev->name, sizeof(xendev->name), "%s-%d", + xendev->type, xendev->dev); + free(dom0); + + xendev->debug = debug; + xendev->local_port = -1; + + xendev->evtchndev = xc_evtchn_open(); + if (xendev->evtchndev < 0) { + xen_be_printf(NULL, 0, "can''t open evtchn device\n"); + qemu_free(xendev); + return NULL; + } + fcntl(xc_evtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC); + + if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { + xendev->gnttabdev = xc_gnttab_open(); + if (xendev->gnttabdev < 0) { + xen_be_printf(NULL, 0, "can''t open gnttab device\n"); + xc_evtchn_close(xendev->evtchndev); + qemu_free(xendev); + return NULL; + } + } else { + xendev->gnttabdev = -1; + } + + TAILQ_INSERT_TAIL(&xendevs, xendev, next); + + if (xendev->ops->alloc) + xendev->ops->alloc(xendev); + + return xendev; +} + +/* + * release xen backend device. + */ +static struct XenDevice *xen_be_del_xendev(int dom, int dev) +{ + struct XenDevice *xendev, *xnext; + + /* + * This is pretty much like TAILQ_FOREACH(xendev, &xendevs, next) but + * we save the next pointer in xnext because we might free xendev. + */ + xnext = xendevs.tqh_first; + while (xnext) { + xendev = xnext; + xnext = xendev->next.tqe_next; + + if (xendev->dom != dom) + continue; + if (xendev->dev != dev && dev != -1) + continue; + + if (xendev->ops->free) + xendev->ops->free(xendev); + + if (xendev->fe) { + char token[XEN_BUFSIZE]; + snprintf(token, sizeof(token), "fe:%p", xendev); + xs_unwatch(xenstore, xendev->fe, token); + qemu_free(xendev->fe); + } + + if (xendev->evtchndev >= 0) + xc_evtchn_close(xendev->evtchndev); + if (xendev->gnttabdev >= 0) + xc_gnttab_close(xendev->gnttabdev); + + TAILQ_REMOVE(&xendevs, xendev, next); + qemu_free(xendev); + } + return NULL; +} + +/* + * Sync internal data structures on xenstore updates. + * Node specifies the changed field. node = NULL means + * update all fields (used for initialization). + */ +static void xen_be_backend_changed(struct XenDevice *xendev, const char *node) +{ + if (node == NULL || strcmp(node, "online") == 0) { + if (xenstore_read_be_int(xendev, "online", &xendev->online) == -1) + xendev->online = 0; + } + + if (node) { + xen_be_printf(xendev, 2, "backend update: %s\n", node); + if (xendev->ops->backend_changed) + xendev->ops->backend_changed(xendev, node); + } +} + +static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node) +{ + int fe_state; + + if (node == NULL || strcmp(node, "state") == 0) { + if (xenstore_read_fe_int(xendev, "state", &fe_state) == -1) + fe_state = XenbusStateUnknown; + if (xendev->fe_state != fe_state) + xen_be_printf(xendev, 1, "frontend state: %s -> %s\n", + xenbus_strstate(xendev->fe_state), + xenbus_strstate(fe_state)); + xendev->fe_state = fe_state; + } + if (node == NULL || strcmp(node, "protocol") == 0) { + qemu_free(xendev->protocol); + xendev->protocol = xenstore_read_fe_str(xendev, "protocol"); + if (xendev->protocol) + xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol); + } + + if (node) { + xen_be_printf(xendev, 2, "frontend update: %s\n", node); + if (xendev->ops->frontend_changed) + xendev->ops->frontend_changed(xendev, node); + } +} + +/* ------------------------------------------------------------- */ +/* Check for possible state transitions and perform them. */ + +/* + * Initial xendev setup. Read frontend path, register watch for it. + * Should succeed once xend finished setting up the backend device. + * + * Also sets initial state (-> Initializing) when done. Which + * only affects the xendev->be_state variable as xenbus should + * already be put into that state by xend. + */ +static int xen_be_try_setup(struct XenDevice *xendev) +{ + char token[XEN_BUFSIZE]; + int be_state; + + if (xenstore_read_be_int(xendev, "state", &be_state) == -1) { + xen_be_printf(xendev, 0, "reading backend state failed\n"); + return -1; + } + + if (be_state != XenbusStateInitialising) { + xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n", + xenbus_strstate(be_state)); + return -1; + } + + xendev->fe = xenstore_read_be_str(xendev, "frontend"); + if (xendev->fe == NULL) { + xen_be_printf(xendev, 0, "reading frontend path failed\n"); + return -1; + } + + /* setup frontend watch */ + snprintf(token, sizeof(token), "fe:%p", xendev); + if (!xs_watch(xenstore, xendev->fe, token)) { + xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n", + xendev->fe); + return -1; + } + xen_be_set_state(xendev, XenbusStateInitialising); + + xen_be_backend_changed(xendev, NULL); + xen_be_frontend_changed(xendev, NULL); + return 0; +} + +/* + * Try initialize xendev. Prepare everything the backend can do + * without synchronizing with the frontend. Fakes hotplug-status. No + * hotplug involved here because this is about userspace drivers, thus + * there are kernel backend devices which could invoke hotplug. + * + * Goes to InitWait on success. + */ +static int xen_be_try_init(struct XenDevice *xendev) +{ + int rc = 0; + + if (!xendev->online) { + xen_be_printf(xendev, 1, "not online\n"); + return -1; + } + + if (xendev->ops->init) + rc = xendev->ops->init(xendev); + if (rc != 0) { + xen_be_printf(xendev, 1, "init() failed\n"); + return rc; + } + + xenstore_write_be_str(xendev, "hotplug-status", "connected"); + xen_be_set_state(xendev, XenbusStateInitWait); + return 0; +} + +/* + * Try to connect xendev. Depends on the frontend being ready + * for it (shared ring and evtchn info in xenstore, state being + * Initialised or Connected). + * + * Goes to Connected on success. + */ +static int xen_be_try_connect(struct XenDevice *xendev) +{ + int rc = 0; + + if (xendev->fe_state != XenbusStateInitialised && + xendev->fe_state != XenbusStateConnected) { + if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { + xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); + } else { + xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); + return -1; + } + } + + if (xendev->ops->connect) + rc = xendev->ops->connect(xendev); + if (rc != 0) { + xen_be_printf(xendev, 0, "connect() failed\n"); + return rc; + } + + xen_be_set_state(xendev, XenbusStateConnected); + return 0; +} + +/* + * Teardown connection. + * + * Goes to Closed when done. + */ +static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state) +{ + if (xendev->be_state != XenbusStateClosing && + xendev->be_state != XenbusStateClosed && + xendev->ops->disconnect) + xendev->ops->disconnect(xendev); + if (xendev->be_state != state) + xen_be_set_state(xendev, state); +} + +/* + * Try to reset xendev, for reconnection by another frontend instance. + */ +static int xen_be_try_reset(struct XenDevice *xendev) +{ + if (xendev->fe_state != XenbusStateInitialising) + return -1; + + xen_be_printf(xendev, 1, "device reset (for re-connect)\n"); + xen_be_set_state(xendev, XenbusStateInitialising); + return 0; +} + +/* + * state change dispatcher function + */ +void xen_be_check_state(struct XenDevice *xendev) +{ + int rc = 0; + + /* frontend may request shutdown from almost anywhere */ + if (xendev->fe_state == XenbusStateClosing || + xendev->fe_state == XenbusStateClosed) { + xen_be_disconnect(xendev, xendev->fe_state); + return; + } + + /* check for possible backend state transitions */ + for (;;) { + switch (xendev->be_state) { + case XenbusStateUnknown: + rc = xen_be_try_setup(xendev); + break; + case XenbusStateInitialising: + rc = xen_be_try_init(xendev); + break; + case XenbusStateInitWait: + rc = xen_be_try_connect(xendev); + break; + case XenbusStateClosed: + rc = xen_be_try_reset(xendev); + break; + default: + rc = -1; + } + if (rc != 0) + break; + } +} + +/* ------------------------------------------------------------- */ + +static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char path[XEN_BUFSIZE], token[XEN_BUFSIZE]; + char **dev = NULL, *dom0; + unsigned int cdev, j; + + /* setup watch */ + dom0 = xs_get_domain_path(xenstore, 0); + snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops); + snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom); + free(dom0); + if (!xs_watch(xenstore, path, token)) { + xen_be_printf(NULL, 0, "xen be: watching backend path (%s) failed\n", path); + return -1; + } + + /* look for backends */ + dev = xs_directory(xenstore, 0, path, &cdev); + if (!dev) + return 0; + for (j = 0; j < cdev; j++) { + xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops); + if (xendev == NULL) + continue; + xen_be_check_state(xendev); + } + free(dev); + return 0; +} + +static void xenstore_update_be(char *watch, char *type, int dom, + struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char path[XEN_BUFSIZE], *dom0; + unsigned int len, dev; + + dom0 = xs_get_domain_path(xenstore, 0); + len = snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom); + free(dom0); + if (strncmp(path, watch, len) != 0) + return; + if (sscanf(watch+len, "/%u/%255s", &dev, path) != 2) { + strcpy(path, ""); + if (sscanf(watch+len, "/%u", &dev) != 1) + dev = -1; + } + if (dev == -1) + return; + + if (0) { + /* FIXME: detect devices being deleted from xenstore ... */ + xen_be_del_xendev(dom, dev); + } + + xendev = xen_be_get_xendev(type, dom, dev, ops); + if (xendev != NULL) { + xen_be_backend_changed(xendev, path); + xen_be_check_state(xendev); + } +} + +static void xenstore_update_fe(char *watch, struct XenDevice *xendev) +{ + char *node; + unsigned int len; + + len = strlen(xendev->fe); + if (strncmp(xendev->fe, watch, len) != 0) + return; + if (watch[len] != ''/'') + return; + node = watch + len + 1; + + xen_be_frontend_changed(xendev, node); + xen_be_check_state(xendev); +} + +static void xenstore_update(void *unused) +{ + char **vec = NULL; + intptr_t type, ops, ptr; + unsigned int dom, count; + + vec = xs_read_watch(xenstore, &count); + if (vec == NULL) + goto cleanup; + + if (sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR, + &type, &dom, &ops) == 3) + xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops); + if (sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr) == 1) + xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr); + +cleanup: + qemu_free(vec); +} + +static void xen_be_evtchn_event(void *opaque) +{ + struct XenDevice *xendev = opaque; + evtchn_port_t port; + + port = xc_evtchn_pending(xendev->evtchndev); + if (port != xendev->local_port) { + xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n", + port, xendev->local_port); + return; + } + xc_evtchn_unmask(xendev->evtchndev, port); + + if (xendev->ops->event) + xendev->ops->event(xendev); +} + +/* -------------------------------------------------------------------- */ + +int xen_be_init(void) +{ + xenstore = xs_daemon_open(); + if (!xenstore) { + xen_be_printf(NULL, 0, "can''t connect to xenstored\n"); + return -1; + } + + if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) + goto err; + + xen_xc = xc_interface_open(); + if (xen_xc == -1) { + xen_be_printf(NULL, 0, "can''t open xen interface\n"); + goto err; + } + return 0; + +err: + qemu_set_fd_handler(xs_fileno(xenstore), NULL, NULL, NULL); + xs_daemon_close(xenstore); + xenstore = NULL; + + return -1; +} + +int xen_be_register(const char *type, struct XenDevOps *ops) +{ + return xenstore_scan(type, xen_domid, ops); +} + +int xen_be_bind_evtchn(struct XenDevice *xendev) +{ + if (xendev->local_port != -1) + return 0; + xendev->local_port = xc_evtchn_bind_interdomain + (xendev->evtchndev, xendev->dom, xendev->remote_port); + if (xendev->local_port == -1) { + xen_be_printf(xendev, 0, "xc_evtchn_bind_interdomain failed\n"); + return -1; + } + xen_be_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), + xen_be_evtchn_event, NULL, xendev); + return 0; +} + +void xen_be_unbind_evtchn(struct XenDevice *xendev) +{ + if (xendev->local_port == -1) + return; + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL); + xc_evtchn_unbind(xendev->evtchndev, xendev->local_port); + xen_be_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port); + xendev->local_port = -1; +} + +int xen_be_send_notify(struct XenDevice *xendev) +{ + return xc_evtchn_notify(xendev->evtchndev, xendev->local_port); +} + +/* + * msg_level: + * 0 == errors (stderr + logfile). + * 1 == informative debug messages (logfile only). + * 2 == noisy debug messages (logfile only). + * 3 == will flood your log (logfile only). + */ +void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) +{ + va_list args; + + if (xendev) { + if (msg_level > xendev->debug) + return; + qemu_log("xen be: %s: ", xendev->name); + if (msg_level == 0) + fprintf(stderr, "xen be: %s: ", xendev->name); + } else { + if (msg_level > debug) + return; + qemu_log("xen be core: "); + if (msg_level == 0) + fprintf(stderr, "xen be core: "); + } + va_start(args, fmt); + qemu_log_vprintf(fmt, args); + va_end(args); + if (msg_level == 0) { + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + } + qemu_log_flush(); +} diff --git a/hw/xen_backend.h b/hw/xen_backend.h new file mode 100644 index 0000000..a1243f6 --- /dev/null +++ b/hw/xen_backend.h @@ -0,0 +1,86 @@ +#ifndef QEMU_HW_XEN_BACKEND_H +#define QEMU_HW_XEN_BACKEND_H 1 + +#include "xen_common.h" + +/* ------------------------------------------------------------- */ + +#define XEN_BUFSIZE 1024 + +struct XenDevice; + +/* driver uses grant tables -> open gntdev device (xendev->gnttabdev) */ +#define DEVOPS_FLAG_NEED_GNTDEV 1 +/* don''t expect frontend doing correct state transitions (aka console quirk) */ +#define DEVOPS_FLAG_IGNORE_STATE 2 + +struct XenDevOps { + size_t size; + uint32_t flags; + void (*alloc)(struct XenDevice *xendev); + int (*init)(struct XenDevice *xendev); + int (*connect)(struct XenDevice *xendev); + void (*event)(struct XenDevice *xendev); + void (*disconnect)(struct XenDevice *xendev); + int (*free)(struct XenDevice *xendev); + void (*backend_changed)(struct XenDevice *xendev, const char *node); + void (*frontend_changed)(struct XenDevice *xendev, const char *node); +}; + +struct XenDevice { + const char *type; + int dom; + int dev; + char name[64]; + int debug; + + enum xenbus_state be_state; + enum xenbus_state fe_state; + int online; + char be[XEN_BUFSIZE]; + char *fe; + char *protocol; + int remote_port; + int local_port; + + int evtchndev; + int gnttabdev; + + struct XenDevOps *ops; + TAILQ_ENTRY(XenDevice) next; +}; + +/* ------------------------------------------------------------- */ + +/* variables */ +extern int xen_xc; +extern struct xs_handle *xenstore; + +/* xenstore helper functions */ +int xenstore_write_str(const char *base, const char *node, const char *val); +int xenstore_write_int(const char *base, const char *node, int ival); +char *xenstore_read_str(const char *base, const char *node); +int xenstore_read_int(const char *base, const char *node, int *ival); + +int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val); +int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival); +char *xenstore_read_be_str(struct XenDevice *xendev, const char *node); +int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival); +char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node); +int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival); + +const char *xenbus_strstate(enum xenbus_state state); +struct XenDevice *xen_be_find_xendev(const char *type, int dom, int dev); +void xen_be_check_state(struct XenDevice *xendev); + +/* xen backend driver bits */ +int xen_be_init(void); +int xen_be_register(const char *type, struct XenDevOps *ops); +int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state); +int xen_be_bind_evtchn(struct XenDevice *xendev); +void xen_be_unbind_evtchn(struct XenDevice *xendev); +int xen_be_send_notify(struct XenDevice *xendev); +void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) + __attribute__ ((format(printf, 3, 4))); + +#endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_common.h b/hw/xen_common.h new file mode 100644 index 0000000..7562567 --- /dev/null +++ b/hw/xen_common.h @@ -0,0 +1,34 @@ +#ifndef QEMU_HW_XEN_COMMON_H +#define QEMU_HW_XEN_COMMON_H 1 + +#include <stddef.h> +#include <inttypes.h> + +#include <xenctrl.h> +#include <xs.h> +#include <xen/io/xenbus.h> + +#include "hw.h" +#include "xen.h" +#include "sys-queue.h" /* BSD list implementation */ + +/* + * tweaks needed to build with different xen versions + * 0x00030205 -> 3.1.0 + * 0x00030207 -> 3.2.0 + * 0x00030208 -> unstable + */ +#include <xen/xen-compat.h> +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030205 +# define evtchn_port_or_error_t int +#endif +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030207 +# define xc_map_foreign_pages xc_map_foreign_batch +#endif +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030208 +# define xen_mb() mb() +# define xen_rmb() rmb() +# define xen_wmb() wmb() +#endif + +#endif /* QEMU_HW_XEN_COMMON_H */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index f32ba7f..a6070fd 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -26,7 +26,7 @@ #include "pc.h" #include "sysemu.h" #include "boards.h" -#include "xen.h" +#include "xen_backend.h" uint32_t xen_domid; enum xen_mode xen_mode = XEN_EMULATE; @@ -50,6 +50,12 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, } env = cpu_init(cpu_model); env->halted = 1; + + /* Initialize backend core & drivers */ + if (xen_be_init() != 0) { + fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); + exit(1); + } } QEMUMachine xenpv_machine = { -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-21 12:20 UTC
[Xen-devel] [PATCH 03/10] xen: add console backend driver.
This patch adds a xenconsole backend driver. It it based on current xen-unstable code. It has been changed to make use of the common backend driver code. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 1 + hw/xen_backend.h | 3 + hw/xen_console.c | 270 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_machine_pv.c | 1 + 4 files changed, 275 insertions(+), 0 deletions(-) create mode 100644 hw/xen_console.c diff --git a/Makefile.target b/Makefile.target index 52c8cd2..1cb86f9 100644 --- a/Makefile.target +++ b/Makefile.target @@ -562,6 +562,7 @@ endif # xen backend driver support XEN_OBJS := xen_machine_pv.o xen_backend.o +XEN_OBJS += xen_console.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.h b/hw/xen_backend.h index a1243f6..4744713 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -83,4 +83,7 @@ int xen_be_send_notify(struct XenDevice *xendev); void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) __attribute__ ((format(printf, 3, 4))); +/* actual backend drivers */ +extern struct XenDevOps xen_console_ops; /* xen_console.c */ + #endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_console.c b/hw/xen_console.c new file mode 100644 index 0000000..27f809d --- /dev/null +++ b/hw/xen_console.c @@ -0,0 +1,270 @@ +/* + * Copyright (C) International Business Machines Corp., 2005 + * Author(s): Anthony Liguori <aliguori@us.ibm.com> + * + * Copyright (C) Red Hat 2007 + * + * Xen Console + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/select.h> +#include <fcntl.h> +#include <unistd.h> +#include <termios.h> +#include <stdarg.h> +#include <sys/mman.h> +#include <xs.h> +#include <xen/io/console.h> +#include <xenctrl.h> + +#include "hw.h" +#include "sysemu.h" +#include "qemu-char.h" +#include "xen_backend.h" + +struct buffer { + uint8_t *data; + size_t consumed; + size_t size; + size_t capacity; + size_t max_capacity; +}; + +struct XenConsole { + struct XenDevice xendev; /* must be first */ + struct buffer buffer; + char console[XEN_BUFSIZE]; + int ring_ref; + void *sring; + CharDriverState *chr; + int backlog; +}; + +static void buffer_append(struct XenConsole *con) +{ + struct buffer *buffer = &con->buffer; + XENCONS_RING_IDX cons, prod, size; + struct xencons_interface *intf = con->sring; + + cons = intf->out_cons; + prod = intf->out_prod; + xen_mb(); + + size = prod - cons; + if ((size == 0) || (size > sizeof(intf->out))) + return; + + if ((buffer->capacity - buffer->size) < size) { + buffer->capacity += (size + 1024); + buffer->data = qemu_realloc(buffer->data, buffer->capacity); + } + + while (cons != prod) + buffer->data[buffer->size++] = intf->out[ + MASK_XENCONS_IDX(cons++, intf->out)]; + + xen_mb(); + intf->out_cons = cons; + xen_be_send_notify(&con->xendev); + + if (buffer->max_capacity && + buffer->size > buffer->max_capacity) { + /* Discard the middle of the data. */ + + size_t over = buffer->size - buffer->max_capacity; + uint8_t *maxpos = buffer->data + buffer->max_capacity; + + memmove(maxpos - over, maxpos, over); + buffer->data = qemu_realloc(buffer->data, buffer->max_capacity); + buffer->size = buffer->capacity = buffer->max_capacity; + + if (buffer->consumed > buffer->max_capacity - over) + buffer->consumed = buffer->max_capacity - over; + } +} + +static void buffer_advance(struct buffer *buffer, size_t len) +{ + buffer->consumed += len; + if (buffer->consumed == buffer->size) { + buffer->consumed = 0; + buffer->size = 0; + } +} + +static int ring_free_bytes(struct XenConsole *con) +{ + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX cons, prod, space; + + cons = intf->in_cons; + prod = intf->in_prod; + xen_mb(); + + space = prod - cons; + if (space > sizeof(intf->in)) + return 0; /* ring is screwed: ignore it */ + + return (sizeof(intf->in) - space); +} + +static int xencons_can_receive(void *opaque) +{ + struct XenConsole *con = opaque; + return ring_free_bytes(con); +} + +static void xencons_receive(void *opaque, const uint8_t *buf, int len) +{ + struct XenConsole *con = opaque; + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX prod; + int i, max; + + max = ring_free_bytes(con); + /* The can_receive() func limits this, but check again anyway */ + if (max < len) + len = max; + + prod = intf->in_prod; + for (i = 0; i < len; i++) { + intf->in[MASK_XENCONS_IDX(prod++, intf->in)] + buf[i]; + } + xen_wmb(); + intf->in_prod = prod; + xen_be_send_notify(&con->xendev); +} + +static void xencons_send(struct XenConsole *con) +{ + ssize_t len, size; + + size = con->buffer.size - con->buffer.consumed; + if (con->chr) + len = qemu_chr_write(con->chr, con->buffer.data + con->buffer.consumed, + size); + else + len = size; + if (len < 1) { + if (!con->backlog) { + con->backlog = 1; + xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n"); + } + } else { + buffer_advance(&con->buffer, len); + if (con->backlog && len == size) { + con->backlog = 0; + xen_be_printf(&con->xendev, 1, "backlog is gone\n"); + } + } +} + +/* -------------------------------------------------------------------- */ + +static int con_init(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + char *type, *dom; + + /* setup */ + dom = xs_get_domain_path(xenstore, con->xendev.dom); + snprintf(con->console, sizeof(con->console), "%s/console", dom); + free(dom); + + type = xenstore_read_str(con->console, "type"); + if (!type || 0 != strcmp(type, "ioemu")) { + xen_be_printf(xendev, 1, "not for me (type=%s)\n", type); + return -1; + } + + if (!serial_hds[con->xendev.dev]) + xen_be_printf(xendev, 1, "WARNING: serial line %d not configured\n", + con->xendev.dev); + else + con->chr = serial_hds[con->xendev.dev]; + + return 0; +} + +static int con_connect(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + int limit; + + if (xenstore_read_int(con->console, "ring-ref", &con->ring_ref) == -1) + return -1; + if (xenstore_read_int(con->console, "port", &con->xendev.remote_port) == -1) + return -1; + if (xenstore_read_int(con->console, "limit", &limit) == 0) + con->buffer.max_capacity = limit; + + con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom, + XC_PAGE_SIZE, + PROT_READ|PROT_WRITE, + con->ring_ref); + if (!con->sring) + return -1; + + xen_be_bind_evtchn(&con->xendev); + if (con->chr) + qemu_chr_add_handlers(con->chr, xencons_can_receive, xencons_receive, + NULL, con); + + xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", + con->ring_ref, + con->xendev.remote_port, + con->xendev.local_port, + con->buffer.max_capacity); + return 0; +} + +static void con_disconnect(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + + if (con->chr) + qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); + xen_be_unbind_evtchn(&con->xendev); + + if (con->sring) { + munmap(con->sring, XC_PAGE_SIZE); + con->sring = NULL; + } +} + +static void con_event(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + + buffer_append(con); + if (con->buffer.size - con->buffer.consumed) + xencons_send(con); +} + +/* -------------------------------------------------------------------- */ + +struct XenDevOps xen_console_ops = { + .size = sizeof(struct XenConsole), + .flags = DEVOPS_FLAG_IGNORE_STATE, + .init = con_init, + .connect = con_connect, + .event = con_event, + .disconnect = con_disconnect, +}; diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index a6070fd..93ab312 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -56,6 +56,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); exit(1); } + xen_be_register("console", &xen_console_ops); } QEMUMachine xenpv_machine = { -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-21 12:20 UTC
[Xen-devel] [PATCH 04/10] xen: add framebuffer backend driver
This patch adds a frsamebuffer (and kbd+mouse) backend driver. It it based on current xen-unstable code. It has been changed to make use of the common backend driver code. It also has been changed to compile with xen headers older than release 3.3 Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen_backend.h | 4 + hw/xen_machine_pv.c | 5 + hw/xenfb.c | 1013 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1023 insertions(+), 1 deletions(-) create mode 100644 hw/xenfb.c diff --git a/Makefile.target b/Makefile.target index 1cb86f9..8734190 100644 --- a/Makefile.target +++ b/Makefile.target @@ -562,7 +562,7 @@ endif # xen backend driver support XEN_OBJS := xen_machine_pv.o xen_backend.o -XEN_OBJS += xen_console.o +XEN_OBJS += xen_console.o xenfb.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.h b/hw/xen_backend.h index 4744713..e9a4e2d 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -85,5 +85,9 @@ void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ... /* actual backend drivers */ extern struct XenDevOps xen_console_ops; /* xen_console.c */ +extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ +extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ + +void xen_init_display(int domid); #endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 93ab312..1f0b3ec 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -57,6 +57,11 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, exit(1); } xen_be_register("console", &xen_console_ops); + xen_be_register("vkbd", &xen_kbdmouse_ops); + xen_be_register("vfb", &xen_framebuffer_ops); + + /* setup framebuffer */ + xen_init_display(xen_domid); } QEMUMachine xenpv_machine = { diff --git a/hw/xenfb.c b/hw/xenfb.c new file mode 100644 index 0000000..d938441 --- /dev/null +++ b/hw/xenfb.c @@ -0,0 +1,1013 @@ +/* + * xen paravirt framebuffer backend + * + * Copyright IBM, Corp. 2005-2006 + * Copyright Red Hat, Inc. 2006-2008 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com>, + * Markus Armbruster <armbru@redhat.com>, + * Daniel P. Berrange <berrange@redhat.com>, + * Pat Campbell <plc@novell.com>, + * Gerd Hoffmann <kraxel@redhat.com> + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdbool.h> +#include <sys/mman.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/event_channel.h> +#include <xen/io/xenbus.h> +#include <xen/io/fbif.h> +#include <xen/io/kbdif.h> +#include <xen/io/protocols.h> + +#include "hw.h" +#include "sysemu.h" +#include "console.h" +#include "qemu-char.h" +#include "xen_backend.h" + +#ifndef BTN_LEFT +#define BTN_LEFT 0x110 /* from <linux/input.h> */ +#endif + +/* -------------------------------------------------------------------- */ + +struct common { + struct XenDevice xendev; /* must be first */ + void *page; + DisplayState *ds; +}; + +struct XenInput { + struct common c; + int abs_pointer_wanted; /* Whether guest supports absolute pointer */ + int button_state; /* Last seen pointer button state */ + int extended; + QEMUPutMouseEntry *qmouse; +}; + +#define UP_QUEUE 8 + +struct XenFB { + struct common c; + size_t fb_len; + int row_stride; + int depth; + int width; + int height; + int offset; + void *pixels; + int fbpages; + int feature_update; + int refresh_period; + int bug_trigger; + int have_console; + int do_resize; + + struct { + int x,y,w,h; + } up_rects[UP_QUEUE]; + int up_count; + int up_fullscreen; +}; + +/* -------------------------------------------------------------------- */ + +static int common_bind(struct common *c) +{ + int mfn; + + if (xenstore_read_fe_int(&c->xendev, "page-ref", &mfn) == -1) + return -1; + if (xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port) == -1) + return -1; + + c->page = xc_map_foreign_range(xen_xc, c->xendev.dom, + XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, mfn); + if (c->page == NULL) + return -1; + + xen_be_bind_evtchn(&c->xendev); + xen_be_printf(&c->xendev, 1, "ring mfn %d, remote-port %d, local-port %d\n", + mfn, c->xendev.remote_port, c->xendev.local_port); + + return 0; +} + +static void common_unbind(struct common *c) +{ + xen_be_unbind_evtchn(&c->xendev); + if (c->page) { + munmap(c->page, XC_PAGE_SIZE); + c->page = NULL; + } +} + +/* -------------------------------------------------------------------- */ + +#if 0 +/* + * These two tables are not needed any more, but left in here + * intentionally as documentation, to show how scancode2linux[] + * was generated. + * + * Tables to map from scancode to Linux input layer keycode. + * Scancodes are hardware-specific. These maps assumes a + * standard AT or PS/2 keyboard which is what QEMU feeds us. + */ +const unsigned char atkbd_set2_keycode[512] = { + + 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, + 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, + 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, + 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, + 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, + 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85, + 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0, + 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, + 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, + 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142, + 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, + 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, + +}; + +const unsigned char atkbd_unxlate_table[128] = { + + 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, + 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, + 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, + 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, + 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 + +}; +#endif + +/* + * for (i = 0; i < 128; i++) { + * scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; + * scancode2linux[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; + * } + */ +static const unsigned char scancode2linux[512] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 99, 0, 86, 87, 88,117, 0, 0, 95,183,184,185, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 93, 0, 0, 89, 0, 0, 85, 91, 90, 92, 0, 94, 0,124,121, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 165, 0, 0, 0, 0, 0, 0, 0, 0,163, 0, 0, 96, 97, 0, 0, + 113,140,164, 0,166, 0, 0, 0, 0, 0,255, 0, 0, 0,114, 0, + 115, 0,150, 0, 0, 98,255, 99,100, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,119,119,102,103,104, 0,105,112,106,118,107, + 108,109,110,111, 0, 0, 0, 0, 0, 0, 0,125,126,127,116,142, + 0, 0, 0,143, 0,217,156,173,128,159,158,157,155,226, 0,112, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* Send an event to the keyboard frontend driver */ +static int xenfb_kbd_event(struct XenInput *xenfb, + union xenkbd_in_event *event) +{ + struct xenkbd_page *page = xenfb->c.page; + uint32_t prod; + + if (xenfb->c.xendev.be_state != XenbusStateConnected) + return 0; + if (!page) + return 0; + + prod = page->in_prod; + if (prod - page->in_cons == XENKBD_IN_RING_LEN) { + errno = EAGAIN; + return -1; + } + + xen_mb(); /* ensure ring space available */ + XENKBD_IN_RING_REF(page, prod) = *event; + xen_wmb(); /* ensure ring contents visible */ + page->in_prod = prod + 1; + return xen_be_send_notify(&xenfb->c.xendev); +} + +/* Send a keyboard (or mouse button) event */ +static int xenfb_send_key(struct XenInput *xenfb, bool down, int keycode) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_KEY; + event.key.pressed = down ? 1 : 0; + event.key.keycode = keycode; + + return xenfb_kbd_event(xenfb, &event); +} + +/* Send a relative mouse movement event */ +static int xenfb_send_motion(struct XenInput *xenfb, + int rel_x, int rel_y, int rel_z) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_MOTION; + event.motion.rel_x = rel_x; + event.motion.rel_y = rel_y; +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207 + event.motion.rel_z = rel_z; +#endif + + return xenfb_kbd_event(xenfb, &event); +} + +/* Send an absolute mouse movement event */ +static int xenfb_send_position(struct XenInput *xenfb, + int abs_x, int abs_y, int z) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_POS; + event.pos.abs_x = abs_x; + event.pos.abs_y = abs_y; +#if __XEN_LATEST_INTERFACE_VERSION__ == 0x00030207 + event.pos.abs_z = z; +#endif +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030208 + event.pos.rel_z = z; +#endif + + return xenfb_kbd_event(xenfb, &event); +} + +/* + * Send a key event from the client to the guest OS + * QEMU gives us a raw scancode from an AT / PS/2 style keyboard. + * We have to turn this into a Linux Input layer keycode. + * + * Extra complexity from the fact that with extended scancodes + * (like those produced by arrow keys) this method gets called + * twice, but we only want to send a single event. So we have to + * track the ''0xe0'' scancode state & collapse the extended keys + * as needed. + * + * Wish we could just send scancodes straight to the guest which + * already has code for dealing with this... + */ +static void xenfb_key_event(void *opaque, int scancode) +{ + struct XenInput *xenfb = opaque; + int down = 1; + + if (scancode == 0xe0) { + xenfb->extended = 1; + return; + } else if (scancode & 0x80) { + scancode &= 0x7f; + down = 0; + } + if (xenfb->extended) { + scancode |= 0x80; + xenfb->extended = 0; + } + xenfb_send_key(xenfb, down, scancode2linux[scancode]); +} + +/* + * Send a mouse event from the client to the guest OS + * + * The QEMU mouse can be in either relative, or absolute mode. + * Movement is sent separately from button state, which has to + * be encoded as virtual key events. We also don''t actually get + * given any button up/down events, so have to track changes in + * the button state. + */ +static void xenfb_mouse_event(void *opaque, + int dx, int dy, int dz, int button_state) +{ + struct XenInput *xenfb = opaque; + int dw = ds_get_width(xenfb->c.ds); + int dh = ds_get_height(xenfb->c.ds); + int i; + + if (xenfb->abs_pointer_wanted) + xenfb_send_position(xenfb, + dx * (dw - 1) / 0x7fff, + dy * (dh - 1) / 0x7fff, + dz); + else + xenfb_send_motion(xenfb, dx, dy, dz); + + for (i = 0 ; i < 8 ; i++) { + int lastDown = xenfb->button_state & (1 << i); + int down = button_state & (1 << i); + if (down == lastDown) + continue; + + if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0) + return; + } + xenfb->button_state = button_state; +} + +static int input_init(struct XenDevice *xendev) +{ + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); + + if (!in->c.ds) { + xen_be_printf(xendev, 1, "ds not set (yet)\n"); + return -1; + } + + xenstore_write_be_int(xendev, "feature-abs-pointer", 1); + return 0; +} + +static int input_connect(struct XenDevice *xendev) +{ + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); + int rc; + + if (xenstore_read_fe_int(xendev, "request-abs-pointer", + &in->abs_pointer_wanted) == -1) + in->abs_pointer_wanted = 0; + + rc = common_bind(&in->c); + if (rc != 0) + return rc; + + qemu_add_kbd_event_handler(xenfb_key_event, in); + in->qmouse = qemu_add_mouse_event_handler(xenfb_mouse_event, in, + in->abs_pointer_wanted, + "Xen PVFB Mouse"); + return 0; +} + +static void input_disconnect(struct XenDevice *xendev) +{ + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); + + if (in->qmouse) { + qemu_remove_mouse_event_handler(in->qmouse); + in->qmouse = NULL; + } + qemu_add_kbd_event_handler(NULL, NULL); + common_unbind(&in->c); +} + +static void input_event(struct XenDevice *xendev) +{ + struct XenInput *xenfb = container_of(xendev, struct XenInput, c.xendev); + struct xenkbd_page *page = xenfb->c.page; + + /* We don''t understand any keyboard events, so just ignore them. */ + if (page->out_prod == page->out_cons) + return; + page->out_cons = page->out_prod; + xen_be_send_notify(&xenfb->c.xendev); +} + +/* -------------------------------------------------------------------- */ + +static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src) +{ + uint32_t *src32 = src; + uint64_t *src64 = src; + int i; + + for (i = 0; i < count; i++) + dst[i] = (mode == 32) ? src32[i] : src64[i]; +} + +static int xenfb_map_fb(struct XenFB *xenfb) +{ + struct xenfb_page *page = xenfb->c.page; + char *protocol = xenfb->c.xendev.protocol; + int n_fbdirs; + unsigned long *pgmfns = NULL; + unsigned long *fbmfns = NULL; + void *map, *pd; + int mode, ret = -1; + + /* default to native */ + pd = page->pd; + mode = sizeof(unsigned long) * 8; + + if (!protocol) { + /* + * Undefined protocol, some guesswork needed. + * + * Old frontends which don''t set the protocol use + * one page directory only, thus pd[1] must be zero. + * pd[1] of the 32bit struct layout and the lower + * 32 bits of pd[0] of the 64bit struct layout have + * the same location, so we can check that ... + */ + uint32_t *ptr32 = NULL; + uint32_t *ptr64 = NULL; +#if defined(__i386__) + ptr32 = (void*)page->pd; + ptr64 = ((void*)page->pd) + 4; +#elif defined(__x86_64__) + ptr32 = ((void*)page->pd) - 4; + ptr64 = (void*)page->pd; +#endif + if (ptr32) { + if (ptr32[1] == 0) { + mode = 32; + pd = ptr32; + } else { + mode = 64; + pd = ptr64; + } + } +#if defined(__x86_64__) + } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { + /* 64bit dom0, 32bit domU */ + mode = 32; + pd = ((void*)page->pd) - 4; +#elif defined(__i386__) + } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { + /* 32bit dom0, 64bit domU */ + mode = 64; + pd = ((void*)page->pd) + 4; +#endif + } + + if (xenfb->pixels) { + munmap(xenfb->pixels, xenfb->fbpages * XC_PAGE_SIZE); + xenfb->pixels = NULL; + } + + xenfb->fbpages = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + n_fbdirs = xenfb->fbpages * mode / 8; + n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + + pgmfns = qemu_mallocz(sizeof(unsigned long) * n_fbdirs); + fbmfns = qemu_mallocz(sizeof(unsigned long) * xenfb->fbpages); + + xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd); + map = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, + PROT_READ, pgmfns, n_fbdirs); + if (map == NULL) + goto out; + xenfb_copy_mfns(mode, xenfb->fbpages, fbmfns, map); + munmap(map, n_fbdirs * XC_PAGE_SIZE); + + xenfb->pixels = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, + PROT_READ | PROT_WRITE, fbmfns, xenfb->fbpages); + if (xenfb->pixels == NULL) + goto out; + + ret = 0; /* all is fine */ + +out: + qemu_free(pgmfns); + qemu_free(fbmfns); + return ret; +} + +static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim, + int width, int height, int depth, + size_t fb_len, int offset, int row_stride) +{ + size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd); + size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz; + size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz; + size_t fb_len_max = fb_pages * XC_PAGE_SIZE; + int max_width, max_height; + + if (fb_len_lim > fb_len_max) { + xen_be_printf(&xenfb->c.xendev, 0, "fb size limit %zu exceeds %zu, corrected\n", + fb_len_lim, fb_len_max); + fb_len_lim = fb_len_max; + } + if (fb_len_lim && fb_len > fb_len_lim) { + xen_be_printf(&xenfb->c.xendev, 0, "frontend fb size %zu limited to %zu\n", + fb_len, fb_len_lim); + fb_len = fb_len_lim; + } + if (depth != 8 && depth != 16 && depth != 24 && depth != 32) { + xen_be_printf(&xenfb->c.xendev, 0, "can''t handle frontend fb depth %d\n", + depth); + return -1; + } + if (row_stride <= 0 || row_stride > fb_len) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n", row_stride); + return -1; + } + max_width = row_stride / (depth / 8); + if (width < 0 || width > max_width) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend width %d limited to %d\n", + width, max_width); + width = max_width; + } + if (offset < 0 || offset >= fb_len) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend offset %d (max %zu)\n", + offset, fb_len - 1); + return -1; + } + max_height = (fb_len - offset) / row_stride; + if (height < 0 || height > max_height) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend height %d limited to %d\n", + height, max_height); + height = max_height; + } + xenfb->fb_len = fb_len; + xenfb->row_stride = row_stride; + xenfb->depth = depth; + xenfb->width = width; + xenfb->height = height; + xenfb->offset = offset; + xenfb->up_fullscreen = 1; + xenfb->do_resize = 1; + xen_be_printf(&xenfb->c.xendev, 1, "framebuffer %dx%dx%d offset %d stride %d\n", + width, height, depth, offset, row_stride); + return 0; +} + +/* A convenient function for munging pixels between different depths */ +#define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \ + for (line = y ; line < (y+h) ; line++) { \ + SRC_T *src = (SRC_T *)(xenfb->pixels \ + + xenfb->offset \ + + (line * xenfb->row_stride) \ + + (x * xenfb->depth / 8)); \ + DST_T *dst = (DST_T *)(data \ + + (line * linesize) \ + + (x * bpp / 8)); \ + int col; \ + const int RSS = 32 - (RSB + GSB + BSB); \ + const int GSS = 32 - (GSB + BSB); \ + const int BSS = 32 - (BSB); \ + const uint32_t RSM = (~0U) << (32 - RSB); \ + const uint32_t GSM = (~0U) << (32 - GSB); \ + const uint32_t BSM = (~0U) << (32 - BSB); \ + const int RDS = 32 - (RDB + GDB + BDB); \ + const int GDS = 32 - (GDB + BDB); \ + const int BDS = 32 - (BDB); \ + const uint32_t RDM = (~0U) << (32 - RDB); \ + const uint32_t GDM = (~0U) << (32 - GDB); \ + const uint32_t BDM = (~0U) << (32 - BDB); \ + for (col = x ; col < (x+w) ; col++) { \ + uint32_t spix = *src; \ + *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \ + (((spix << GSS) & GSM & GDM) >> GDS) | \ + (((spix << BSS) & BSM & BDM) >> BDS); \ + src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \ + dst = (DST_T *) ((unsigned long) dst + bpp / 8); \ + } \ + } + + +/* + * This copies data from the guest framebuffer region, into QEMU''s + * displaysurface. qemu uses 16 or 32 bpp. In case the pv framebuffer + * uses something else we must convert and copy, otherwise we can + * supply the buffer directly and no thing here. + */ +static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h) +{ + int line, oops = 0; + int bpp = ds_get_bits_per_pixel(xenfb->c.ds); + int linesize = ds_get_linesize(xenfb->c.ds); + uint8_t *data = ds_get_data(xenfb->c.ds); + + if (!is_buffer_shared(xenfb->c.ds->surface)) { + switch (xenfb->depth) { + case 8: + if (bpp == 16) { + BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5); + } else if (bpp == 32) { + BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8); + } else { + oops = 1; + } + break; + case 24: + if (bpp == 16) { + BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5); + } else if (bpp == 32) { + BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8); + } else { + oops = 1; + } + break; + default: + oops = 1; + } + } + if (oops) /* should not happen */ + xen_be_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n", + __FUNCTION__, xenfb->depth, bpp); + + dpy_update(xenfb->c.ds, x, y, w, h); +} + +#ifdef XENFB_TYPE_REFRESH_PERIOD +static int xenfb_queue_full(struct XenFB *xenfb) +{ + struct xenfb_page *page = xenfb->c.page; + uint32_t cons, prod; + + if (!page) + return 1; + + prod = page->in_prod; + cons = page->in_cons; + return prod - cons == XENFB_IN_RING_LEN; +} + +static void xenfb_send_event(struct XenFB *xenfb, union xenfb_in_event *event) +{ + uint32_t prod; + struct xenfb_page *page = xenfb->c.page; + + prod = page->in_prod; + /* caller ensures !xenfb_queue_full() */ + xen_mb(); /* ensure ring space available */ + XENFB_IN_RING_REF(page, prod) = *event; + xen_wmb(); /* ensure ring contents visible */ + page->in_prod = prod + 1; + + xen_be_send_notify(&xenfb->c.xendev); +} + +static void xenfb_send_refresh_period(struct XenFB *xenfb, int period) +{ + union xenfb_in_event event; + + memset(&event, 0, sizeof(event)); + event.type = XENFB_TYPE_REFRESH_PERIOD; + event.refresh_period.period = period; + xenfb_send_event(xenfb, &event); +} +#endif + +/* + * Periodic update of display. + * Also transmit the refresh interval to the frontend. + * + * Never ever do any qemu display operations + * (resize, screen update) outside this function. + * Our screen might be inactive. When asked for + * an update we know it is active. + */ +static void xenfb_update(void *opaque) +{ + struct XenFB *xenfb = opaque; + struct DisplayChangeListener *l; + int i; + + if (xenfb->c.xendev.be_state != XenbusStateConnected) + return; + + if (xenfb->feature_update) { +#ifdef XENFB_TYPE_REFRESH_PERIOD + int period = 99999999; + int idle = 1; + + if (xenfb_queue_full(xenfb)) + return; + + for (l = xenfb->c.ds->listeners; l != NULL; l = l->next) { + if (l->idle) + continue; + idle = 0; + if (!l->gui_timer_interval) { + if (period > GUI_REFRESH_INTERVAL) + period = GUI_REFRESH_INTERVAL; + } else { + if (period > l->gui_timer_interval) + period = l->gui_timer_interval; + } + } + if (idle) + period = XENFB_NO_REFRESH; + + if (xenfb->refresh_period != period) { + xenfb_send_refresh_period(xenfb, period); + xenfb->refresh_period = period; + xen_be_printf(&xenfb->c.xendev, 1, "refresh period: %d\n", period); + } +#else + ; /* nothing */ +#endif + } else { + /* we don''t get update notifications, thus use the + * sledge hammer approach ... */ + xenfb->up_fullscreen = 1; + } + + /* resize if needed */ + if (xenfb->do_resize) { + xenfb->do_resize = 0; + switch (xenfb->depth) { + case 16: + case 32: + /* console.c supported depth -> buffer can be used directly */ + qemu_free_displaysurface(xenfb->c.ds); + xenfb->c.ds->surface = qemu_create_displaysurface_from + (xenfb->width, xenfb->height, xenfb->depth, + xenfb->row_stride, xenfb->pixels + xenfb->offset); + break; + default: + /* we must convert stuff */ + qemu_resize_displaysurface(xenfb->c.ds, xenfb->width, xenfb->height); + break; + } + xen_be_printf(&xenfb->c.xendev, 1, "update: resizing: %dx%d @ %d bpp%s\n", + xenfb->width, xenfb->height, xenfb->depth, + is_buffer_shared(xenfb->c.ds->surface) ? " (shared)" : ""); + dpy_resize(xenfb->c.ds); + xenfb->up_fullscreen = 1; + } + + /* run queued updates */ + if (xenfb->up_fullscreen) { + xen_be_printf(&xenfb->c.xendev, 3, "update: fullscreen\n"); + xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height); + } else if (xenfb->up_count) { + xen_be_printf(&xenfb->c.xendev, 3, "update: %d rects\n", xenfb->up_count); + for (i = 0; i < xenfb->up_count; i++) + xenfb_guest_copy(xenfb, + xenfb->up_rects[i].x, + xenfb->up_rects[i].y, + xenfb->up_rects[i].w, + xenfb->up_rects[i].h); + } else { + xen_be_printf(&xenfb->c.xendev, 3, "update: nothing\n"); + } + xenfb->up_count = 0; + xenfb->up_fullscreen = 0; +} + +/* QEMU display state changed, so refresh the framebuffer copy */ +static void xenfb_invalidate(void *opaque) +{ + struct XenFB *xenfb = opaque; + xenfb->up_fullscreen = 1; +} + +static void xenfb_handle_events(struct XenFB *xenfb) +{ + uint32_t prod, cons; + struct xenfb_page *page = xenfb->c.page; + + prod = page->out_prod; + if (prod == page->out_cons) + return; + xen_rmb(); /* ensure we see ring contents up to prod */ + for (cons = page->out_cons; cons != prod; cons++) { + union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons); + int x, y, w, h; + + switch (event->type) { + case XENFB_TYPE_UPDATE: + if (xenfb->up_count == UP_QUEUE) + xenfb->up_fullscreen = 1; + if (xenfb->up_fullscreen) + break; + x = MAX(event->update.x, 0); + y = MAX(event->update.y, 0); + w = MIN(event->update.width, xenfb->width - x); + h = MIN(event->update.height, xenfb->height - y); + if (w < 0 || h < 0) { + xen_be_printf(&xenfb->c.xendev, 1, "bogus update ignored\n"); + break; + } + if (x != event->update.x || + y != event->update.y || + w != event->update.width || + h != event->update.height) { + xen_be_printf(&xenfb->c.xendev, 1, "bogus update clipped\n"); + } + if (w == xenfb->width && h > xenfb->height / 2) { + /* scroll detector: updated more than 50% of the lines, + * don''t bother keeping track of the rectangles then */ + xenfb->up_fullscreen = 1; + } else { + xenfb->up_rects[xenfb->up_count].x = x; + xenfb->up_rects[xenfb->up_count].y = y; + xenfb->up_rects[xenfb->up_count].w = w; + xenfb->up_rects[xenfb->up_count].h = h; + xenfb->up_count++; + } + break; +#ifdef XENFB_TYPE_RESIZE + case XENFB_TYPE_RESIZE: + if (xenfb_configure_fb(xenfb, xenfb->fb_len, + event->resize.width, + event->resize.height, + event->resize.depth, + xenfb->fb_len, + event->resize.offset, + event->resize.stride) < 0) + break; + xenfb_invalidate(xenfb); + break; +#endif + } + } + xen_mb(); /* ensure we''re done with ring contents */ + page->out_cons = cons; +} + +static int fb_init(struct XenDevice *xendev) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + + fb->refresh_period = -1; + +#ifdef XENFB_TYPE_RESIZE + xenstore_write_be_int(xendev, "feature-resize", 1); +#endif + return 0; +} + +static int fb_connect(struct XenDevice *xendev) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + struct xenfb_page *fb_page; + int videoram; + int rc; + + if (xenstore_read_fe_int(xendev, "videoram", &videoram) == -1) + videoram = 0; + + rc = common_bind(&fb->c); + if (rc != 0) + return rc; + + fb_page = fb->c.page; + rc = xenfb_configure_fb(fb, videoram * 1024 * 1024U, + fb_page->width, fb_page->height, fb_page->depth, + fb_page->mem_length, 0, fb_page->line_length); + if (rc != 0) + return rc; + + rc = xenfb_map_fb(fb); + if (rc != 0) + return rc; + +#if 0 /* handled in xen_init_display() for now */ + if (!fb->have_console) { + fb->c.ds = graphic_console_init(xenfb_update, + xenfb_invalidate, + NULL, + NULL, + fb); + fb->have_console = 1; + } +#endif + + if (xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update) == -1) + fb->feature_update = 0; + if (fb->feature_update) + xenstore_write_be_int(xendev, "request-update", 1); + + xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n", + fb->feature_update, videoram); + return 0; +} + +static void fb_disconnect(struct XenDevice *xendev) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + + /* + * FIXME: qemu can''t un-init gfx display (yet?). + * Replacing the framebuffer with anonymous shared memory + * instead. This releases the guest pages and keeps qemu happy. + */ + fb->pixels = mmap(fb->pixels, fb->fbpages * XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, + -1, 0); + common_unbind(&fb->c); + fb->feature_update = 0; + fb->bug_trigger = 0; +} + +static void fb_frontend_changed(struct XenDevice *xendev, const char *node) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + + /* + * Set state to Connected *again* once the frontend switched + * to connected. We must trigger the watch a second time to + * workaround a frontend bug. + */ + if (fb->bug_trigger == 0 && strcmp(node, "state") == 0 && + xendev->fe_state == XenbusStateConnected && + xendev->be_state == XenbusStateConnected) { + xen_be_printf(xendev, 2, "re-trigger connected (frontend bug)\n"); + xen_be_set_state(xendev, XenbusStateConnected); + fb->bug_trigger = 1; /* only once */ + } +} + +static void fb_event(struct XenDevice *xendev) +{ + struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev); + + xenfb_handle_events(xenfb); + xen_be_send_notify(&xenfb->c.xendev); +} + +/* -------------------------------------------------------------------- */ + +struct XenDevOps xen_kbdmouse_ops = { + .size = sizeof(struct XenInput), + .init = input_init, + .connect = input_connect, + .disconnect = input_disconnect, + .event = input_event, +}; + +struct XenDevOps xen_framebuffer_ops = { + .size = sizeof(struct XenFB), + .init = fb_init, + .connect = fb_connect, + .disconnect = fb_disconnect, + .event = fb_event, + .frontend_changed = fb_frontend_changed, +}; + +/* + * FIXME/TODO: Kill this. + * Temporary needed while DisplayState reorganization is in flight. + */ +void xen_init_display(int domid) +{ + struct XenDevice *xfb, *xin; + struct XenFB *fb; + struct XenInput *in; + int i = 0; + +wait_more: + i++; + main_loop_wait(10); /* miliseconds */ + xfb = xen_be_find_xendev("vfb", domid, 0); + xin = xen_be_find_xendev("vkbd", domid, 0); + if (!xfb || !xin) { + if (i < 256) + goto wait_more; + xen_be_printf(NULL, 1, "displaystate setup failed\n"); + return; + } + + /* vfb */ + fb = container_of(xfb, struct XenFB, c.xendev); + fb->c.ds = graphic_console_init(xenfb_update, + xenfb_invalidate, + NULL, + NULL, + fb); + fb->have_console = 1; + + /* vkbd */ + in = container_of(xin, struct XenInput, c.xendev); + in->c.ds = fb->c.ds; + + /* retry ->init() */ + xen_be_check_state(xin); + xen_be_check_state(xfb); +} -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-21 12:20 UTC
[Xen-devel] [PATCH 05/10] xen: add block device backend driver.
This patch adds a block device backend driver to qemu. It is a pure userspace implemention using the gntdev interface. It uses "qdisk" as backend name in xenstore so it doesn''t interfere with the other existing backends (blkback aka "vbd" and tapdisk aka "tap"). Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen_backend.h | 2 + hw/xen_blkif.h | 103 +++++++ hw/xen_disk.c | 779 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_machine_pv.c | 1 + sysemu.h | 2 +- vl.c | 6 +- 7 files changed, 892 insertions(+), 3 deletions(-) create mode 100644 hw/xen_blkif.h create mode 100644 hw/xen_disk.c diff --git a/Makefile.target b/Makefile.target index 8734190..ea3b9d5 100644 --- a/Makefile.target +++ b/Makefile.target @@ -562,7 +562,7 @@ endif # xen backend driver support XEN_OBJS := xen_machine_pv.o xen_backend.o -XEN_OBJS += xen_console.o xenfb.o +XEN_OBJS += xen_console.o xenfb.o xen_disk.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.h b/hw/xen_backend.h index e9a4e2d..dd426dd 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -2,6 +2,7 @@ #define QEMU_HW_XEN_BACKEND_H 1 #include "xen_common.h" +#include "sysemu.h" /* ------------------------------------------------------------- */ @@ -87,6 +88,7 @@ void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ... extern struct XenDevOps xen_console_ops; /* xen_console.c */ extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ +extern struct XenDevOps xen_blkdev_ops; /* xen_disk.c */ void xen_init_display(int domid); diff --git a/hw/xen_blkif.h b/hw/xen_blkif.h new file mode 100644 index 0000000..254a5fd --- /dev/null +++ b/hw/xen_blkif.h @@ -0,0 +1,103 @@ +#ifndef __XEN_BLKIF_H__ +#define __XEN_BLKIF_H__ + +#include <xen/io/ring.h> +#include <xen/io/blkif.h> +#include <xen/io/protocols.h> + +/* Not a real protocol. Used to generate ring structs which contain + * the elements common to all protocols only. This way we get a + * compiler-checkable way to use common struct elements, so we can + * avoid using switch(protocol) in a number of places. */ +struct blkif_common_request { + char dummy; +}; +struct blkif_common_response { + char dummy; +}; + +/* i386 protocol version */ +#pragma pack(push, 4) +struct blkif_x86_32_request { + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t id; /* private guest value, echoed in resp */ + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; +struct blkif_x86_32_response { + uint64_t id; /* copied from request */ + uint8_t operation; /* copied from request */ + int16_t status; /* BLKIF_RSP_??? */ +}; +typedef struct blkif_x86_32_request blkif_x86_32_request_t; +typedef struct blkif_x86_32_response blkif_x86_32_response_t; +#pragma pack(pop) + +/* x86_64 protocol version */ +struct blkif_x86_64_request { + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t __attribute__((__aligned__(8))) id; + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; +struct blkif_x86_64_response { + uint64_t __attribute__((__aligned__(8))) id; + uint8_t operation; /* copied from request */ + int16_t status; /* BLKIF_RSP_??? */ +}; +typedef struct blkif_x86_64_request blkif_x86_64_request_t; +typedef struct blkif_x86_64_response blkif_x86_64_response_t; + +DEFINE_RING_TYPES(blkif_common, struct blkif_common_request, struct blkif_common_response); +DEFINE_RING_TYPES(blkif_x86_32, struct blkif_x86_32_request, struct blkif_x86_32_response); +DEFINE_RING_TYPES(blkif_x86_64, struct blkif_x86_64_request, struct blkif_x86_64_response); + +union blkif_back_rings { + blkif_back_ring_t native; + blkif_common_back_ring_t common; + blkif_x86_32_back_ring_t x86_32; + blkif_x86_64_back_ring_t x86_64; +}; +typedef union blkif_back_rings blkif_back_rings_t; + +enum blkif_protocol { + BLKIF_PROTOCOL_NATIVE = 1, + BLKIF_PROTOCOL_X86_32 = 2, + BLKIF_PROTOCOL_X86_64 = 3, +}; + +static void inline blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_request_t *src) +{ + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; + + dst->operation = src->operation; + dst->nr_segments = src->nr_segments; + dst->handle = src->handle; + dst->id = src->id; + dst->sector_number = src->sector_number; + if (n > src->nr_segments) + n = src->nr_segments; + for (i = 0; i < n; i++) + dst->seg[i] = src->seg[i]; +} + +static void inline blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_request_t *src) +{ + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; + + dst->operation = src->operation; + dst->nr_segments = src->nr_segments; + dst->handle = src->handle; + dst->id = src->id; + dst->sector_number = src->sector_number; + if (n > src->nr_segments) + n = src->nr_segments; + for (i = 0; i < n; i++) + dst->seg[i] = src->seg[i]; +} + +#endif /* __XEN_BLKIF_H__ */ diff --git a/hw/xen_disk.c b/hw/xen_disk.c new file mode 100644 index 0000000..527f84b --- /dev/null +++ b/hw/xen_disk.c @@ -0,0 +1,779 @@ +/* + * xen paravirt block device backend + * + * (c) Gerd Hoffmann <kraxel@redhat.com> + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <inttypes.h> +#include <time.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/uio.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> + +#include "hw.h" +#include "block_int.h" +#include "qemu-char.h" +#include "xen_blkif.h" +#include "xen_backend.h" + +/* ------------------------------------------------------------- */ + +static int syncwrite = 0; +static int batch_maps = 0; + +static int max_requests = 32; +static int use_aio = 1; + +/* ------------------------------------------------------------- */ + +#define BLOCK_SIZE 512 +#define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2) + +struct ioreq { + blkif_request_t req; + int16_t status; + + /* parsed request */ + off_t start; + QEMUIOVector v; + int presync; + int postsync; + + /* grant mapping */ + uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + int prot; + void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + void *pages; + + /* aio status */ + int aio_inflight; + int aio_errors; + + struct XenBlkDev *blkdev; + LIST_ENTRY(ioreq) list; +}; + +struct XenBlkDev { + struct XenDevice xendev; /* must be first */ + char *params; + char *mode; + char *type; + char *dev; + char *devtype; + const char *fileproto; + const char *filename; + int ring_ref; + void *sring; + int64_t file_blk; + int64_t file_size; + int protocol; + blkif_back_rings_t rings; + int more_work; + int cnt_map; + + /* request lists */ + LIST_HEAD(inflight_head, ioreq) inflight; + LIST_HEAD(finished_head, ioreq) finished; + LIST_HEAD(freelist_head, ioreq) freelist; + int requests_total; + int requests_inflight; + int requests_finished; + + /* qemu block driver */ + int index; + BlockDriverState *bs; + QEMUBH *bh; +}; + +/* ------------------------------------------------------------- */ + +static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) +{ + struct ioreq *ioreq = NULL; + + if (LIST_EMPTY(&blkdev->freelist)) { + if (blkdev->requests_total >= max_requests) + goto out; + /* allocate new struct */ + ioreq = qemu_mallocz(sizeof(*ioreq)); + ioreq->blkdev = blkdev; + blkdev->requests_total++; + qemu_iovec_init(&ioreq->v, BLKIF_MAX_SEGMENTS_PER_REQUEST); + } else { + /* get one from freelist */ + ioreq = LIST_FIRST(&blkdev->freelist); + LIST_REMOVE(ioreq, list); + qemu_iovec_reset(&ioreq->v); + } + LIST_INSERT_HEAD(&blkdev->inflight, ioreq, list); + blkdev->requests_inflight++; + +out: + return ioreq; +} + +static void ioreq_finish(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + + LIST_REMOVE(ioreq, list); + LIST_INSERT_HEAD(&blkdev->finished, ioreq, list); + blkdev->requests_inflight--; + blkdev->requests_finished++; +} + +static void ioreq_release(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + + LIST_REMOVE(ioreq, list); + memset(ioreq, 0, sizeof(*ioreq)); + ioreq->blkdev = blkdev; + LIST_INSERT_HEAD(&blkdev->freelist, ioreq, list); + blkdev->requests_finished--; +} + +/* + * translate request into iovec + start offset + * do sanity checks along the way + */ +static int ioreq_parse(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + uintptr_t mem; + size_t len; + int i; + + xen_be_printf(&blkdev->xendev, 3, + "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", + ioreq->req.operation, ioreq->req.nr_segments, + ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + ioreq->prot = PROT_WRITE; /* to memory */ + if (BLKIF_OP_READ != ioreq->req.operation && blkdev->mode[0] != ''w'') { + xen_be_printf(&blkdev->xendev, 0, "error: write req for ro device\n"); + goto err; + } + break; + case BLKIF_OP_WRITE_BARRIER: + if (!syncwrite) + ioreq->presync = ioreq->postsync = 1; + /* fall through */ + case BLKIF_OP_WRITE: + ioreq->prot = PROT_READ; /* from memory */ + if (syncwrite) + ioreq->postsync = 1; + break; + default: + xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", + ioreq->req.operation); + goto err; + }; + + ioreq->start = ioreq->req.sector_number * blkdev->file_blk; + for (i = 0; i < ioreq->req.nr_segments; i++) { + if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { + xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); + goto err; + } + if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { + xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n"); + goto err; + } + if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { + xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n"); + goto err; + } + + ioreq->domids[i] = blkdev->xendev.dom; + ioreq->refs[i] = ioreq->req.seg[i].gref; + + mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; + len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; + qemu_iovec_add(&ioreq->v, (void*)mem, len); + } + if (ioreq->start + ioreq->v.size > blkdev->file_size) { + xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); + goto err; + } + return 0; + +err: + ioreq->status = BLKIF_RSP_ERROR; + return -1; +} + +static void ioreq_unmap(struct ioreq *ioreq) +{ + int gnt = ioreq->blkdev->xendev.gnttabdev; + int i; + + if (ioreq->v.niov == 0) + return; + if (batch_maps) { + if (!ioreq->pages) + return; + if (xc_gnttab_munmap(gnt, ioreq->pages, ioreq->v.niov) != 0) + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + ioreq->blkdev->cnt_map -= ioreq->v.niov; + ioreq->pages = NULL; + } else { + for (i = 0; i < ioreq->v.niov; i++) { + if (!ioreq->page[i]) + continue; + if (xc_gnttab_munmap(gnt, ioreq->page[i], 1) != 0) + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + ioreq->blkdev->cnt_map--; + ioreq->page[i] = NULL; + } + } +} + +static int ioreq_map(struct ioreq *ioreq) +{ + int gnt = ioreq->blkdev->xendev.gnttabdev; + int i; + + if (ioreq->v.niov == 0) + return 0; + if (batch_maps) { + ioreq->pages = xc_gnttab_map_grant_refs + (gnt, ioreq->v.niov, ioreq->domids, ioreq->refs, ioreq->prot); + if (ioreq->pages == NULL) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can''t map %d grant refs (%s, %d maps)\n", + ioreq->v.niov, strerror(errno), ioreq->blkdev->cnt_map); + return -1; + } + for (i = 0; i < ioreq->v.niov; i++) + ioreq->v.iov[i].iov_base = ioreq->pages + i * XC_PAGE_SIZE + + (uintptr_t)ioreq->v.iov[i].iov_base; + ioreq->blkdev->cnt_map += ioreq->v.niov; + } else { + for (i = 0; i < ioreq->v.niov; i++) { + ioreq->page[i] = xc_gnttab_map_grant_ref + (gnt, ioreq->domids[i], ioreq->refs[i], ioreq->prot); + if (ioreq->page[i] == NULL) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can''t map grant ref %d (%s, %d maps)\n", + ioreq->refs[i], strerror(errno), ioreq->blkdev->cnt_map); + ioreq_unmap(ioreq); + return -1; + } + ioreq->v.iov[i].iov_base = ioreq->page[i] + (uintptr_t)ioreq->v.iov[i].iov_base; + ioreq->blkdev->cnt_map++; + } + } + return 0; +} + +static int ioreq_runio_qemu_sync(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + int i, rc, len = 0; + off_t pos; + + if (ioreq_map(ioreq) == -1) + goto err; + if (ioreq->presync) + bdrv_flush(blkdev->bs); + + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + pos = ioreq->start; + for (i = 0; i < ioreq->v.niov; i++) { + rc = bdrv_read(blkdev->bs, pos / BLOCK_SIZE, + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "rd I/O error (%p, len %zd)\n", + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len); + goto err; + } + len += ioreq->v.iov[i].iov_len; + pos += ioreq->v.iov[i].iov_len; + } + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_WRITE_BARRIER: + pos = ioreq->start; + for (i = 0; i < ioreq->v.niov; i++) { + rc = bdrv_write(blkdev->bs, pos / BLOCK_SIZE, + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "wr I/O error (%p, len %zd)\n", + ioreq->v.iov[i].iov_base, + ioreq->v.iov[i].iov_len); + goto err; + } + len += ioreq->v.iov[i].iov_len; + pos += ioreq->v.iov[i].iov_len; + } + break; + default: + /* unknown operation (shouldn''t happen -- parse catches this) */ + goto err; + } + + if (ioreq->postsync) + bdrv_flush(blkdev->bs); + ioreq->status = BLKIF_RSP_OKAY; + + ioreq_unmap(ioreq); + ioreq_finish(ioreq); + return 0; + +err: + ioreq->status = BLKIF_RSP_ERROR; + return -1; +} + +static void qemu_aio_complete(void *opaque, int ret) +{ + struct ioreq *ioreq = opaque; + + if (ret != 0) { + xen_be_printf(&ioreq->blkdev->xendev, 0, "%s I/O error\n", + ioreq->req.operation == BLKIF_OP_READ ? "read" : "write"); + ioreq->aio_errors++; + } + + ioreq->aio_inflight--; + if (ioreq->aio_inflight > 0) + return; + + ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; + ioreq_unmap(ioreq); + ioreq_finish(ioreq); + qemu_bh_schedule(ioreq->blkdev->bh); +} + +static int ioreq_runio_qemu_aio(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + + if (ioreq_map(ioreq) == -1) + goto err; + + ioreq->aio_inflight++; + if (ioreq->presync) + bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */ + + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + ioreq->aio_inflight++; + bdrv_aio_readv(blkdev->bs, ioreq->start / BLOCK_SIZE, + &ioreq->v, ioreq->v.size / BLOCK_SIZE, + qemu_aio_complete, ioreq); + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_WRITE_BARRIER: + ioreq->aio_inflight++; + bdrv_aio_writev(blkdev->bs, ioreq->start / BLOCK_SIZE, + &ioreq->v, ioreq->v.size / BLOCK_SIZE, + qemu_aio_complete, ioreq); + break; + default: + /* unknown operation (shouldn''t happen -- parse catches this) */ + goto err; + } + + if (ioreq->postsync) + bdrv_flush(blkdev->bs); /* FIXME: aio_flush() ??? */ + qemu_aio_complete(ioreq, 0); + + return 0; + +err: + ioreq->status = BLKIF_RSP_ERROR; + return -1; +} + +static int blk_send_response_one(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + int send_notify = 0; + int have_requests = 0; + blkif_response_t resp; + void *dst; + + resp.id = ioreq->req.id; + resp.operation = ioreq->req.operation; + resp.status = ioreq->status; + + /* Place on the response ring for the relevant domain. */ + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + dst = RING_GET_RESPONSE(&blkdev->rings.native, blkdev->rings.native.rsp_prod_pvt); + break; + case BLKIF_PROTOCOL_X86_32: + dst = RING_GET_RESPONSE(&blkdev->rings.x86_32, blkdev->rings.x86_32.rsp_prod_pvt); + break; + case BLKIF_PROTOCOL_X86_64: + dst = RING_GET_RESPONSE(&blkdev->rings.x86_64, blkdev->rings.x86_64.rsp_prod_pvt); + break; + default: + dst = NULL; + } + memcpy(dst, &resp, sizeof(resp)); + blkdev->rings.common.rsp_prod_pvt++; + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify); + if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) { + /* + * Tail check for pending requests. Allows frontend to avoid + * notifications if requests are already in flight (lower + * overheads and promotes batching). + */ + RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests); + } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) { + have_requests = 1; + } + + if (have_requests) + blkdev->more_work++; + return send_notify; +} + +/* walk finished list, send outstanding responses, free requests */ +static void blk_send_response_all(struct XenBlkDev *blkdev) +{ + struct ioreq *ioreq; + int send_notify = 0; + + while (!LIST_EMPTY(&blkdev->finished)) { + ioreq = LIST_FIRST(&blkdev->finished); + send_notify += blk_send_response_one(ioreq); + ioreq_release(ioreq); + } + if (send_notify) + xen_be_send_notify(&blkdev->xendev); +} + +static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_IDX rc) +{ + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc), + sizeof(ioreq->req)); + break; + case BLKIF_PROTOCOL_X86_32: + blkif_get_x86_32_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_32, rc)); + break; + case BLKIF_PROTOCOL_X86_64: + blkif_get_x86_64_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_64, rc)); + break; + } + return 0; +} + +static void blk_handle_requests(struct XenBlkDev *blkdev) +{ + RING_IDX rc, rp; + struct ioreq *ioreq; + + blkdev->more_work = 0; + + rc = blkdev->rings.common.req_cons; + rp = blkdev->rings.common.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + if (use_aio) + blk_send_response_all(blkdev); + while ((rc != rp)) { + /* pull request from ring */ + if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) + break; + ioreq = ioreq_start(blkdev); + if (ioreq == NULL) { + blkdev->more_work++; + break; + } + blk_get_request(blkdev, ioreq, rc); + blkdev->rings.common.req_cons = ++rc; + + /* parse them */ + if (ioreq_parse(ioreq) != 0) { + if (blk_send_response_one(ioreq)) + xen_be_send_notify(&blkdev->xendev); + ioreq_release(ioreq); + continue; + } + + if (use_aio) { + /* run i/o in aio mode */ + ioreq_runio_qemu_aio(ioreq); + } else { + /* run i/o in sync mode */ + ioreq_runio_qemu_sync(ioreq); + } + } + if (!use_aio) + blk_send_response_all(blkdev); + + if (blkdev->more_work && blkdev->requests_inflight < max_requests) + qemu_bh_schedule(blkdev->bh); +} + +/* ------------------------------------------------------------- */ + +static void blk_bh(void *opaque) +{ + struct XenBlkDev *blkdev = opaque; + blk_handle_requests(blkdev); +} + +static void blk_alloc(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + LIST_INIT(&blkdev->inflight); + LIST_INIT(&blkdev->finished); + LIST_INIT(&blkdev->freelist); + blkdev->bh = qemu_bh_new(blk_bh, blkdev); + if (xen_mode != XEN_EMULATE) + batch_maps = 1; +} + +static int blk_init(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + int mode, qflags, have_barriers, info = 0; + char *h; + + /* read xenstore entries */ + if (blkdev->params == NULL) { + blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); + h = strchr(blkdev->params, '':''); + if (h != NULL) { + blkdev->fileproto = blkdev->params; + blkdev->filename = h+1; + *h = 0; + } else { + blkdev->fileproto = "<unset>"; + blkdev->filename = blkdev->params; + } + } + if (blkdev->mode == NULL) + blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); + if (blkdev->type == NULL) + blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); + if (blkdev->dev == NULL) + blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); + if (blkdev->devtype == NULL) + blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type"); + + /* do we have all we need? */ + if (blkdev->params == NULL || + blkdev->mode == NULL || + blkdev->type == NULL || + blkdev->dev == NULL) + return -1; + + /* read-only ? */ + if (strcmp(blkdev->mode, "w") == 0) { + mode = O_RDWR; + qflags = BDRV_O_RDWR; + } else { + mode = O_RDONLY; + qflags = BDRV_O_RDONLY; + info |= VDISK_READONLY; + } + + /* cdrom ? */ + if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom")) + info |= VDISK_CDROM; + + /* init qemu block driver */ + blkdev->index = (blkdev->xendev.dev - 202 * 256) / 16; + blkdev->index = drive_get_index(IF_XEN, 0, blkdev->index); + if (blkdev->index == -1) { + /* setup via xenbus -> create new block driver instance */ + xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); + blkdev->bs = bdrv_new(blkdev->dev); + if (blkdev->bs) { + if (bdrv_open2(blkdev->bs, blkdev->filename, qflags, + bdrv_find_format(blkdev->fileproto)) != 0) { + bdrv_delete(blkdev->bs); + blkdev->bs = NULL; + } + } + if (!blkdev->bs) + return -1; + } else { + /* setup via qemu cmdline -> already setup for us */ + xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n"); + blkdev->bs = drives_table[blkdev->index].bdrv; + } + blkdev->file_blk = BLOCK_SIZE; + blkdev->file_size = bdrv_getlength(blkdev->bs); + if (blkdev->file_size < 0) { + xen_be_printf(&blkdev->xendev, 1, "bdrv_getlength: %d (%s) | drv %s\n", + (int)blkdev->file_size, strerror(-blkdev->file_size), + blkdev->bs->drv ? blkdev->bs->drv->format_name : "-"); + blkdev->file_size = 0; + } + have_barriers = blkdev->bs->drv && blkdev->bs->drv->bdrv_flush ? 1 : 0; + + xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," + " size %" PRId64 " (%" PRId64 " MB)\n", + blkdev->type, blkdev->fileproto, blkdev->filename, + blkdev->file_size, blkdev->file_size >> 20); + + /* fill info */ + xenstore_write_be_int(&blkdev->xendev, "feature-barrier", have_barriers); + xenstore_write_be_int(&blkdev->xendev, "info", info); + xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); + xenstore_write_be_int(&blkdev->xendev, "sectors", + blkdev->file_size / blkdev->file_blk); + return 0; +} + +static int blk_connect(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref) == -1) + return -1; + if (xenstore_read_fe_int(&blkdev->xendev, "event-channel", + &blkdev->xendev.remote_port) == -1) + return -1; + + blkdev->protocol = BLKIF_PROTOCOL_NATIVE; + if (blkdev->xendev.protocol) { + if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32) == 0) + blkdev->protocol = BLKIF_PROTOCOL_X86_32; + if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64) == 0) + blkdev->protocol = BLKIF_PROTOCOL_X86_64; + } + + blkdev->sring = xc_gnttab_map_grant_ref(blkdev->xendev.gnttabdev, + blkdev->xendev.dom, + blkdev->ring_ref, + PROT_READ | PROT_WRITE); + if (!blkdev->sring) + return -1; + blkdev->cnt_map++; + + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + { + blkif_sring_t *sring_native = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.native, sring_native, XC_PAGE_SIZE); + break; + } + case BLKIF_PROTOCOL_X86_32: + { + blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.x86_32, sring_x86_32, XC_PAGE_SIZE); + break; + } + case BLKIF_PROTOCOL_X86_64: + { + blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.x86_64, sring_x86_64, XC_PAGE_SIZE); + break; + } + } + + xen_be_bind_evtchn(&blkdev->xendev); + + xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, " + "remote port %d, local port %d\n", + blkdev->xendev.protocol, blkdev->ring_ref, + blkdev->xendev.remote_port, blkdev->xendev.local_port); + return 0; +} + +static void blk_disconnect(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + if (blkdev->bs) { + if (blkdev->index == -1) { + /* close/delete only if we created it ourself */ + bdrv_close(blkdev->bs); + bdrv_delete(blkdev->bs); + } + blkdev->bs = NULL; + } + xen_be_unbind_evtchn(&blkdev->xendev); + + if (blkdev->sring) { + xc_gnttab_munmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); + blkdev->cnt_map--; + blkdev->sring = NULL; + } +} + +static int blk_free(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + struct ioreq *ioreq; + + while (!LIST_EMPTY(&blkdev->freelist)) { + ioreq = LIST_FIRST(&blkdev->freelist); + LIST_REMOVE(ioreq, list); + qemu_iovec_destroy(&ioreq->v); + qemu_free(ioreq); + } + + qemu_free(blkdev->params); + qemu_free(blkdev->mode); + qemu_free(blkdev->type); + qemu_free(blkdev->dev); + qemu_free(blkdev->devtype); + qemu_bh_delete(blkdev->bh); + return 0; +} + +static void blk_event(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + qemu_bh_schedule(blkdev->bh); +} + +struct XenDevOps xen_blkdev_ops = { + .size = sizeof(struct XenBlkDev), + .flags = DEVOPS_FLAG_NEED_GNTDEV, + .alloc = blk_alloc, + .init = blk_init, + .connect = blk_connect, + .disconnect = blk_disconnect, + .event = blk_event, + .free = blk_free, +}; diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 1f0b3ec..c764bea 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -59,6 +59,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, xen_be_register("console", &xen_console_ops); xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); + xen_be_register("qdisk", &xen_blkdev_ops); /* setup framebuffer */ xen_init_display(xen_domid); diff --git a/sysemu.h b/sysemu.h index 24b4bd1..1f1a280 100644 --- a/sysemu.h +++ b/sysemu.h @@ -127,7 +127,7 @@ extern unsigned int nb_prom_envs; #endif typedef enum { - IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO + IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN } BlockInterfaceType; typedef enum { diff --git a/vl.c b/vl.c index b681775..19cd28f 100644 --- a/vl.c +++ b/vl.c @@ -2369,7 +2369,10 @@ int drive_init(struct drive_opt *arg, int snapshot, void *opaque) } else if (!strcmp(buf, "virtio")) { type = IF_VIRTIO; max_devs = 0; - } else { + } else if (!strcmp(buf, "xen")) { + type = IF_XEN; + max_devs = 0; + } else { fprintf(stderr, "qemu: ''%s'' unsupported bus type ''%s''\n", str, buf); return -1; } @@ -2583,6 +2586,7 @@ int drive_init(struct drive_opt *arg, int snapshot, void *opaque) switch(type) { case IF_IDE: case IF_SCSI: + case IF_XEN: switch(media) { case MEDIA_DISK: if (cyls != 0) { -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-21 12:20 UTC
[Xen-devel] [PATCH 06/10] xen: add net backend driver.
This patch adds a network interface backend driver to qemu. It is a pure userspace implemention using the gntdev interface. It uses "qnet" as backend name in xenstore so it doesn''t interfere with the netback backend (aka "vnif"). The network backend is hooked into the corrosponding qemu vlan, i.e. vif 0 is hooked into vlan 0. To make the packages actually arrive somewhere you additionally have to link the vlan to the outside world using the usual qemu command line options such as "-net tap,...". Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen_backend.h | 1 + hw/xen_machine_pv.c | 1 + hw/xen_nic.c | 406 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 409 insertions(+), 1 deletions(-) create mode 100644 hw/xen_nic.c diff --git a/Makefile.target b/Makefile.target index ea3b9d5..8eeccbb 100644 --- a/Makefile.target +++ b/Makefile.target @@ -562,7 +562,7 @@ endif # xen backend driver support XEN_OBJS := xen_machine_pv.o xen_backend.o -XEN_OBJS += xen_console.o xenfb.o xen_disk.o +XEN_OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen_backend.h b/hw/xen_backend.h index dd426dd..4e4be14 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -89,6 +89,7 @@ extern struct XenDevOps xen_console_ops; /* xen_console.c */ extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ extern struct XenDevOps xen_blkdev_ops; /* xen_disk.c */ +extern struct XenDevOps xen_netdev_ops; /* xen_nic.c */ void xen_init_display(int domid); diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index c764bea..c21617a 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -60,6 +60,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); xen_be_register("qdisk", &xen_blkdev_ops); + xen_be_register("qnic", &xen_netdev_ops); /* setup framebuffer */ xen_init_display(xen_domid); diff --git a/hw/xen_nic.c b/hw/xen_nic.c new file mode 100644 index 0000000..2364e82 --- /dev/null +++ b/hw/xen_nic.c @@ -0,0 +1,406 @@ +/* + * xen paravirt network card backend + * + * (c) Gerd Hoffmann <kraxel@redhat.com> + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <inttypes.h> +#include <fcntl.h> +#include <errno.h> +#include <pthread.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/wait.h> +#include <linux/if.h> +#include <linux/if_tun.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> +#include <xen/io/netif.h> + +#include "hw.h" +#include "net.h" +#include "qemu-char.h" +#include "xen_backend.h" + +/* ------------------------------------------------------------- */ + +struct XenNetDev { + struct XenDevice xendev; /* must be first */ + char *mac; + int tx_work; + int tx_ring_ref; + int rx_ring_ref; + struct netif_tx_sring *txs; + struct netif_rx_sring *rxs; + netif_tx_back_ring_t tx_ring; + netif_rx_back_ring_t rx_ring; + VLANClientState *vs; +}; + +/* ------------------------------------------------------------- */ + +static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, int8_t st) +{ + RING_IDX i = netdev->tx_ring.rsp_prod_pvt; + netif_tx_response_t *resp; + int notify; + + resp = RING_GET_RESPONSE(&netdev->tx_ring, i); + resp->id = txp->id; + resp->status = st; + +#if 0 + if (txp->flags & NETTXF_extra_info) + RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL; +#endif + + netdev->tx_ring.rsp_prod_pvt = ++i; + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify); + if (notify) + xen_be_send_notify(&netdev->xendev); + + if (i == netdev->tx_ring.req_cons) { + int more_to_do; + RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do); + if (more_to_do) + netdev->tx_work++; + } +} + +static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING_IDX end) +{ +#if 0 + /* + * Hmm, why netback fails everything in the ring? + * Should we do that even when not supporting SG and TSO? + */ + RING_IDX cons = netdev->tx_ring.req_cons; + + do { + make_tx_response(netif, txp, NETIF_RSP_ERROR); + if (cons >= end) + break; + txp = RING_GET_REQUEST(&netdev->tx_ring, cons++); + } while (1); + netdev->tx_ring.req_cons = cons; + netif_schedule_work(netif); + netif_put(netif); +#else + net_tx_response(netdev, txp, NETIF_RSP_ERROR); +#endif +} + +static void net_tx_packets(struct XenNetDev *netdev) +{ + netif_tx_request_t txreq; + RING_IDX rc, rp; + void *page; + void *tmpbuf = NULL; + + for (;;) { + rc = netdev->tx_ring.req_cons; + rp = netdev->tx_ring.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + while ((rc != rp)) { + if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) + break; + memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq)); + netdev->tx_ring.req_cons = ++rc; + +#if 1 + /* should not happen in theory, we don''t announce the * + * feature-{sg,gso,whatelse} flags in xenstore (yet?) */ + if (txreq.flags & NETTXF_extra_info) { + xen_be_printf(&netdev->xendev, 0, "FIXME: extra info flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_more_data) { + xen_be_printf(&netdev->xendev, 0, "FIXME: more data flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } +#endif + + if (txreq.size < 14) { + xen_be_printf(&netdev->xendev, 0, "bad packet size: %d\n", txreq.size); + net_tx_error(netdev, &txreq, rc); + continue; + } + + if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) { + xen_be_printf(&netdev->xendev, 0, "error: page crossing\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + + xen_be_printf(&netdev->xendev, 3, "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n", + txreq.gref, txreq.offset, txreq.size, txreq.flags, + (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", + (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", + (txreq.flags & NETTXF_more_data) ? " more_data" : "", + (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); + + page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + txreq.gref, PROT_READ); + if (page == NULL) { + xen_be_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n", + txreq.gref); + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_csum_blank) { + /* have read-only mapping -> can''t fill checksum in-place */ + if (!tmpbuf) + tmpbuf = malloc(PAGE_SIZE); + memcpy(tmpbuf, page + txreq.offset, txreq.size); + net_checksum_calculate(tmpbuf, txreq.size); + qemu_send_packet(netdev->vs, tmpbuf, txreq.size); + } else { + qemu_send_packet(netdev->vs, page + txreq.offset, txreq.size); + } + xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); + } + if (!netdev->tx_work) + break; + netdev->tx_work = 0; + } + free(tmpbuf); +} + +/* ------------------------------------------------------------- */ + +static void net_rx_response(struct XenNetDev *netdev, + netif_rx_request_t *req, int8_t st, + uint16_t offset, uint16_t size, + uint16_t flags) +{ + RING_IDX i = netdev->rx_ring.rsp_prod_pvt; + netif_rx_response_t *resp; + int notify; + + resp = RING_GET_RESPONSE(&netdev->rx_ring, i); + resp->offset = offset; + resp->flags = flags; + resp->id = req->id; + resp->status = (int16_t)size; + if (st < 0) + resp->status = (int16_t)st; + + xen_be_printf(&netdev->xendev, 3, "rx response: idx %d, status %d, flags 0x%x\n", + i, resp->status, resp->flags); + + netdev->rx_ring.rsp_prod_pvt = ++i; + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify); + if (notify) + xen_be_send_notify(&netdev->xendev); +} + +#define NET_IP_ALIGN 2 + +static int net_rx_ok(void *opaque) +{ + struct XenNetDev *netdev = opaque; + RING_IDX rc, rp; + + if (netdev->xendev.be_state != XenbusStateConnected) + return 0; + + rc = netdev->rx_ring.req_cons; + rp = netdev->rx_ring.sring->req_prod; + xen_rmb(); + + if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { + xen_be_printf(&netdev->xendev, 2, "%s: no rx buffers (%d/%d)\n", + __FUNCTION__, rc, rp); + return 0; + } + return 1; +} + +static void net_rx_packet(void *opaque, const uint8_t *buf, int size) +{ + struct XenNetDev *netdev = opaque; + netif_rx_request_t rxreq; + RING_IDX rc, rp; + void *page; + + if (netdev->xendev.be_state != XenbusStateConnected) + return; + + rc = netdev->rx_ring.req_cons; + rp = netdev->rx_ring.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { + xen_be_printf(&netdev->xendev, 2, "no buffer, drop packet\n"); + return; + } + if (size > XC_PAGE_SIZE - NET_IP_ALIGN) { + xen_be_printf(&netdev->xendev, 0, "packet too big (%d > %ld)", + size, XC_PAGE_SIZE - NET_IP_ALIGN); + return; + } + + memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); + netdev->rx_ring.req_cons = ++rc; + + page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + rxreq.gref, PROT_WRITE); + if (page == NULL) { + xen_be_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n", + rxreq.gref); + net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0); + return; + } + memcpy(page + NET_IP_ALIGN, buf, size); + xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0); +} + +/* ------------------------------------------------------------- */ + +static int net_init(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + VLANState *vlan; + + /* read xenstore entries */ + if (netdev->mac == NULL) + netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac"); + + /* do we have all we need? */ + if (netdev->mac == NULL) + return -1; + + vlan = qemu_find_vlan(netdev->xendev.dev); + netdev->vs = qemu_new_vlan_client(vlan, "xen", NULL, + net_rx_packet, net_rx_ok, NULL, + netdev); + snprintf(netdev->vs->info_str, sizeof(netdev->vs->info_str), + "nic: xenbus vif macaddr=%s", netdev->mac); + + /* fill info */ + xenstore_write_be_int(&netdev->xendev, "feature-rx-copy", 1); + xenstore_write_be_int(&netdev->xendev, "feature-rx-flip", 0); + + return 0; +} + +static int net_connect(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + int rx_copy; + + if (xenstore_read_fe_int(&netdev->xendev, "tx-ring-ref", + &netdev->tx_ring_ref) == -1) + return -1; + if (xenstore_read_fe_int(&netdev->xendev, "rx-ring-ref", + &netdev->rx_ring_ref) == -1) + return 1; + if (xenstore_read_fe_int(&netdev->xendev, "event-channel", + &netdev->xendev.remote_port) == -1) + return -1; + + if (xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy) == -1) + rx_copy = 0; + if (rx_copy == 0) { + xen_be_printf(&netdev->xendev, 0, "frontend doesn''t support rx-copy.\n"); + return -1; + } + + netdev->txs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + netdev->tx_ring_ref, + PROT_READ | PROT_WRITE); + netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + netdev->rx_ring_ref, + PROT_READ | PROT_WRITE); + if (!netdev->txs || !netdev->rxs) + return -1; + BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE); + BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XC_PAGE_SIZE); + + xen_be_bind_evtchn(&netdev->xendev); + + xen_be_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, " + "remote port %d, local port %d\n", + netdev->tx_ring_ref, netdev->rx_ring_ref, + netdev->xendev.remote_port, netdev->xendev.local_port); + return 0; +} + +static void net_disconnect(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + + xen_be_unbind_evtchn(&netdev->xendev); + + if (netdev->txs) { + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); + netdev->txs = NULL; + } + if (netdev->rxs) { + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1); + netdev->rxs = NULL; + } + if (netdev->vs) { + qemu_del_vlan_client(netdev->vs); + netdev->vs = NULL; + } +} + +static void net_event(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + net_tx_packets(netdev); +} + +static int net_free(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + + qemu_free(netdev->mac); + return 0; +} + +/* ------------------------------------------------------------- */ + +struct XenDevOps xen_netdev_ops = { + .size = sizeof(struct XenNetDev), + .flags = DEVOPS_FLAG_NEED_GNTDEV, + .init = net_init, + .connect = net_connect, + .event = net_event, + .disconnect = net_disconnect, + .free = net_free, +}; -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-21 12:20 UTC
[Xen-devel] [PATCH 07/10] xen: blk & nic configuration via cmd line.
This patch makes qemu create backend and frontend device entries in xenstore for devices configured on the command line. It will use qdisk and qnic backend names, so the qemu internal backends will be used. Disks can be created using -drive if=xen,file=... Nics can be created using -net nic,macaddr=... Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen_backend.c | 1 + hw/xen_backend.h | 8 +++ hw/xen_devconfig.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_machine_pv.c | 19 +++++++ 5 files changed, 171 insertions(+), 1 deletions(-) create mode 100644 hw/xen_devconfig.c diff --git a/Makefile.target b/Makefile.target index 8eeccbb..24d409e 100644 --- a/Makefile.target +++ b/Makefile.target @@ -561,7 +561,7 @@ LIBS += $(CONFIG_BLUEZ_LIBS) endif # xen backend driver support -XEN_OBJS := xen_machine_pv.o xen_backend.o +XEN_OBJS := xen_machine_pv.o xen_backend.o xen_devconfig.o XEN_OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) diff --git a/hw/xen_backend.c b/hw/xen_backend.c index c1e3728..2f2ec7f 100644 --- a/hw/xen_backend.c +++ b/hw/xen_backend.c @@ -45,6 +45,7 @@ /* public */ int xen_xc; struct xs_handle *xenstore = NULL; +const char *xen_protocol; /* private */ static TAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = TAILQ_HEAD_INITIALIZER(xendevs); diff --git a/hw/xen_backend.h b/hw/xen_backend.h index 4e4be14..7f1804e 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -3,6 +3,8 @@ #include "xen_common.h" #include "sysemu.h" +#include "net.h" +#include "block_int.h" /* ------------------------------------------------------------- */ @@ -56,6 +58,7 @@ struct XenDevice { /* variables */ extern int xen_xc; extern struct xs_handle *xenstore; +extern const char *xen_protocol; /* xenstore helper functions */ int xenstore_write_str(const char *base, const char *node, const char *val); @@ -93,4 +96,9 @@ extern struct XenDevOps xen_netdev_ops; /* xen_nic.c */ void xen_init_display(int domid); +/* configuration (aka xenbus setup) */ +void xen_config_cleanup(void); +int xen_config_dev_blk(DriveInfo *disk); +int xen_config_dev_nic(NICInfo *nic); + #endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c new file mode 100644 index 0000000..4420561 --- /dev/null +++ b/hw/xen_devconfig.c @@ -0,0 +1,142 @@ +#include "xen_backend.h" + +/* ------------------------------------------------------------- */ + +struct xs_dirs { + char *xs_dir; + TAILQ_ENTRY(xs_dirs) list; +}; +static TAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup = TAILQ_HEAD_INITIALIZER(xs_cleanup); + +static void xen_config_cleanup_dir(char *dir) +{ + struct xs_dirs *d; + + d = qemu_malloc(sizeof(*d)); + d->xs_dir = dir; + TAILQ_INSERT_TAIL(&xs_cleanup, d, list); +} + +void xen_config_cleanup(void) +{ + struct xs_dirs *d; + + TAILQ_FOREACH(d, &xs_cleanup, list) { + xs_rm(xenstore, 0, d->xs_dir); + } +} + +/* ------------------------------------------------------------- */ + +static int xen_config_dev_mkdir(char *dev, int p) +{ + struct xs_permissions perms[2] = {{ + .id = 0, /* set owner: dom0 */ + },{ + .id = xen_domid, + .perms = p, + }}; + + if (!xs_mkdir(xenstore, 0, dev)) { + xen_be_printf(NULL, 0, "xs_mkdir %s: failed\n", dev); + return -1; + } + xen_config_cleanup_dir(qemu_strdup(dev)); + + if (!xs_set_permissions(xenstore, 0, dev, perms, 2)) { + xen_be_printf(NULL, 0, "xs_set_permissions %s: failed\n", dev); + return -1; + } + return 0; +} + +static int xen_config_dev_dirs(const char *ftype, const char *btype, int vdev, + char *fe, char *be, int len) +{ + char *dom; + + dom = xs_get_domain_path(xenstore, xen_domid); + snprintf(fe, len, "%s/device/%s/%d", dom, ftype, vdev); + free(dom); + + dom = xs_get_domain_path(xenstore, 0); + snprintf(be, len, "%s/backend/%s/%d/%d", dom, btype, xen_domid, vdev); + free(dom); + + xen_config_dev_mkdir(fe, XS_PERM_READ | XS_PERM_WRITE); + xen_config_dev_mkdir(be, XS_PERM_READ); + return 0; +} + +static int xen_config_dev_all(char *fe, char *be) +{ + /* frontend */ + if (xen_protocol) + xenstore_write_str(fe, "protocol", xen_protocol); + + xenstore_write_int(fe, "state", XenbusStateInitialising); + xenstore_write_int(fe, "backend-id", 0); + xenstore_write_str(fe, "backend", be); + + /* backend */ + xenstore_write_str(be, "domain", qemu_name ? qemu_name : "no-name"); + xenstore_write_int(be, "online", 1); + xenstore_write_int(be, "state", XenbusStateInitialising); + xenstore_write_int(be, "frontend-id", xen_domid); + xenstore_write_str(be, "frontend", fe); + + return 0; +} + +/* ------------------------------------------------------------- */ + +int xen_config_dev_blk(DriveInfo *disk) +{ + char fe[256], be[256]; + int vdev = 202 * 256 + 16 * disk->unit; + int cdrom = disk->bdrv->type == BDRV_TYPE_CDROM; + const char *devtype = cdrom ? "cdrom" : "disk"; + const char *mode = cdrom ? "r" : "w"; + + snprintf(disk->bdrv->device_name, sizeof(disk->bdrv->device_name), + "xvd%c", ''a'' + disk->unit); + xen_be_printf(NULL, 1, "config disk %d [%s]: %s\n", + disk->unit, disk->bdrv->device_name, disk->bdrv->filename); + xen_config_dev_dirs("vbd", "qdisk", vdev, fe, be, sizeof(fe)); + + /* frontend */ + xenstore_write_int(fe, "virtual-device", vdev); + xenstore_write_str(fe, "device-type", devtype); + + /* backend */ + xenstore_write_str(be, "dev", disk->bdrv->device_name); + xenstore_write_str(be, "type", "file"); + xenstore_write_str(be, "params", disk->bdrv->filename); + xenstore_write_str(be, "mode", mode); + + /* common stuff */ + return xen_config_dev_all(fe, be); +} + +int xen_config_dev_nic(NICInfo *nic) +{ + char fe[256], be[256]; + char mac[20]; + + snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", + nic->macaddr[0], nic->macaddr[1], nic->macaddr[2], + nic->macaddr[3], nic->macaddr[4], nic->macaddr[5]); + xen_be_printf(NULL, 1, "config nic %d: mac=\"%s\"\n", nic->vlan->id, mac); + xen_config_dev_dirs("vif", "qnic", nic->vlan->id, fe, be, sizeof(fe)); + + /* frontend */ + xenstore_write_int(fe, "handle", nic->vlan->id); + xenstore_write_str(fe, "mac", mac); + + /* backend */ + xenstore_write_int(be, "handle", nic->vlan->id); + xenstore_write_str(be, "mac", mac); + + /* common stuff */ + return xen_config_dev_all(fe, be); +} diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index c21617a..154cf71 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -39,6 +39,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, const char *cpu_model) { CPUState *env; + int i, index; /* Initialize a dummy CPU */ if (cpu_model == NULL) { @@ -62,6 +63,24 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, xen_be_register("qdisk", &xen_blkdev_ops); xen_be_register("qnic", &xen_netdev_ops); + /* configure disks */ + for (i = 0; i < 16; i++) { + index = drive_get_index(IF_XEN, 0, i); + if (index == -1) + continue; + xen_config_dev_blk(drives_table + index); + } + + /* configure nics */ + for (i = 0; i < nb_nics; i++) { + if (!nd_table[i].model || 0 != strcmp(nd_table[i].model, "xen")) + continue; + xen_config_dev_nic(nd_table + i); + } + + /* config cleanup hook */ + atexit(xen_config_cleanup); + /* setup framebuffer */ xen_init_display(xen_domid); } -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
This adds domain building support for paravirtual domains to qemu. This allows booting xen guests directly with qemu, without Xend and the management stack. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- configure | 2 +- hw/xen_backend.h | 3 + hw/xen_devconfig.c | 29 +++++ hw/xen_domainbuild.c | 294 ++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen_domainbuild.h | 13 +++ hw/xen_machine_pv.c | 19 ++++ 7 files changed, 360 insertions(+), 2 deletions(-) create mode 100644 hw/xen_domainbuild.c create mode 100644 hw/xen_domainbuild.h diff --git a/Makefile.target b/Makefile.target index 24d409e..2171587 100644 --- a/Makefile.target +++ b/Makefile.target @@ -561,7 +561,7 @@ LIBS += $(CONFIG_BLUEZ_LIBS) endif # xen backend driver support -XEN_OBJS := xen_machine_pv.o xen_backend.o xen_devconfig.o +XEN_OBJS := xen_machine_pv.o xen_backend.o xen_devconfig.o xen_domainbuild.o XEN_OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) diff --git a/configure b/configure index ec761ff..b7b7b01 100755 --- a/configure +++ b/configure @@ -1634,7 +1634,7 @@ if test "$bluez" = "yes" ; then echo "#define CONFIG_BLUEZ 1" >> $config_h fi if test "$xen" = "yes" ; then - echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak + echo "XEN_LIBS=-lxenstore -lxenctrl -lxenguest" >> $config_mak fi if test "$aio" = "yes" ; then echo "#define CONFIG_AIO 1" >> $config_h diff --git a/hw/xen_backend.h b/hw/xen_backend.h index 7f1804e..4dbfdb4 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -100,5 +100,8 @@ void xen_init_display(int domid); void xen_config_cleanup(void); int xen_config_dev_blk(DriveInfo *disk); int xen_config_dev_nic(NICInfo *nic); +int xen_config_dev_vfb(int vdev, const char *type); +int xen_config_dev_vkbd(int vdev); +int xen_config_dev_console(int vdev); #endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c index 4420561..4121188 100644 --- a/hw/xen_devconfig.c +++ b/hw/xen_devconfig.c @@ -140,3 +140,32 @@ int xen_config_dev_nic(NICInfo *nic) /* common stuff */ return xen_config_dev_all(fe, be); } + +int xen_config_dev_vfb(int vdev, const char *type) +{ + char fe[256], be[256]; + + xen_config_dev_dirs("vfb", "vfb", vdev, fe, be, sizeof(fe)); + + /* backend */ + xenstore_write_str(be, "type", type); + + /* common stuff */ + return xen_config_dev_all(fe, be); +} + +int xen_config_dev_vkbd(int vdev) +{ + char fe[256], be[256]; + + xen_config_dev_dirs("vkbd", "vkbd", vdev, fe, be, sizeof(fe)); + return xen_config_dev_all(fe, be); +} + +int xen_config_dev_console(int vdev) +{ + char fe[256], be[256]; + + xen_config_dev_dirs("console", "console", vdev, fe, be, sizeof(fe)); + return xen_config_dev_all(fe, be); +} diff --git a/hw/xen_domainbuild.c b/hw/xen_domainbuild.c new file mode 100644 index 0000000..21710eb --- /dev/null +++ b/hw/xen_domainbuild.c @@ -0,0 +1,294 @@ +#include <signal.h> +#include "xen_backend.h" +#include "xen_domainbuild.h" +#include "sysemu.h" +#include "qemu-timer.h" + +#include <xenguest.h> + +static int xenstore_domain_mkdir(char *path) +{ + struct xs_permissions perms_ro[] = {{ + .id = 0, /* set owner: dom0 */ + },{ + .id = xen_domid, + .perms = XS_PERM_READ, + }}; + struct xs_permissions perms_rw[] = {{ + .id = 0, /* set owner: dom0 */ + },{ + .id = xen_domid, + .perms = XS_PERM_READ | XS_PERM_WRITE, + }}; + const char *writable[] = { "device", "control", "error", NULL }; + char subpath[256]; + int i; + + if (!xs_mkdir(xenstore, 0, path)) { + fprintf(stderr, "%s: xs_mkdir %s: failed\n", __FUNCTION__, path); + return -1; + } + if (!xs_set_permissions(xenstore, 0, path, perms_ro, 2)) { + fprintf(stderr, "%s: xs_set_permissions failed\n", __FUNCTION__); + return -1; + } + + for (i = 0; writable[i]; i++) { + snprintf(subpath, sizeof(subpath), "%s/%s", path, writable[i]); + if (!xs_mkdir(xenstore, 0, subpath)) { + fprintf(stderr, "%s: xs_mkdir %s: failed\n", __FUNCTION__, subpath); + return -1; + } + if (!xs_set_permissions(xenstore, 0, subpath, perms_rw, 2)) { + fprintf(stderr, "%s: xs_set_permissions failed\n", __FUNCTION__); + return -1; + } + } + return 0; +} + +int xenstore_domain_init1(const char *kernel, const char *ramdisk, + const char *cmdline) +{ + char *dom, uuid_string[42], vm[256], path[256]; + int i; + + snprintf(uuid_string, sizeof(uuid_string), UUID_FMT, + qemu_uuid[0], qemu_uuid[1], qemu_uuid[2], qemu_uuid[3], + qemu_uuid[4], qemu_uuid[5], qemu_uuid[6], qemu_uuid[7], + qemu_uuid[8], qemu_uuid[9], qemu_uuid[10], qemu_uuid[11], + qemu_uuid[12], qemu_uuid[13], qemu_uuid[14], qemu_uuid[15]); + dom = xs_get_domain_path(xenstore, xen_domid); + snprintf(vm, sizeof(vm), "/vm/%s", uuid_string); + + xenstore_domain_mkdir(dom); + + xenstore_write_str(vm, "image/ostype", "linux"); + if (kernel) + xenstore_write_str(vm, "image/kernel", kernel); + if (ramdisk) + xenstore_write_str(vm, "image/ramdisk", ramdisk); + if (cmdline) + xenstore_write_str(vm, "image/cmdline", cmdline); + + /* name + id */ + xenstore_write_str(vm, "name", qemu_name ? qemu_name : "no-name"); + xenstore_write_str(vm, "uuid", uuid_string); + xenstore_write_str(dom, "name", qemu_name ? qemu_name : "no-name"); + xenstore_write_int(dom, "domid", xen_domid); + xenstore_write_str(dom, "vm", vm); + + /* memory */ + xenstore_write_int(dom, "memory/target", ram_size >> 10); // kB + xenstore_write_int(vm, "memory", ram_size >> 20); // MB + xenstore_write_int(vm, "maxmem", ram_size >> 20); // MB + + /* cpus */ + for (i = 0; i < smp_cpus; i++) { + snprintf(path, sizeof(path), "cpu/%d/availability",i); + xenstore_write_str(dom, path, "online"); + } + xenstore_write_int(vm, "vcpu_avail", smp_cpus); + xenstore_write_int(vm, "vcpus", smp_cpus); + + /* vnc password */ + xenstore_write_str(vm, "vncpassword", "" /* FIXME */); + + free(dom); + return 0; +} + +int xenstore_domain_init2(int xenstore_port, int xenstore_mfn, + int console_port, int console_mfn) +{ + char *dom; + + dom = xs_get_domain_path(xenstore, xen_domid); + + /* signal new domain */ + xs_introduce_domain(xenstore, + xen_domid, + xenstore_mfn, + xenstore_port); + + /* xenstore */ + xenstore_write_int(dom, "store/ring-ref", xenstore_mfn); + xenstore_write_int(dom, "store/port", xenstore_port); + + /* console */ + xenstore_write_str(dom, "console/type", "ioemu"); + xenstore_write_int(dom, "console/limit", 128 * 1024); + xenstore_write_int(dom, "console/ring-ref", console_mfn); + xenstore_write_int(dom, "console/port", console_port); + xen_config_dev_console(0); + + free(dom); + return 0; +} + +/* ------------------------------------------------------------- */ + +static QEMUTimer *xen_poll; + +/* check domain state once per second */ +static void xen_domain_poll(void *opaque) +{ + struct xc_dominfo info; + int rc; + + rc = xc_domain_getinfo(xen_xc, xen_domid, 1, &info); + if ((1 != rc) || (info.domid != xen_domid)) { + qemu_log("xen: domain %d is gone\n", xen_domid); + goto quit; + } + if (info.dying) { + qemu_log("xen: domain %d is dying (%s%s)\n", xen_domid, + info.crashed ? "crashed" : "", + info.shutdown ? "shutdown" : ""); + goto quit; + } + + qemu_mod_timer(xen_poll, qemu_get_clock(rt_clock) + 1000); + return; + +quit: + qemu_system_shutdown_request(); + return; +} + +static void xen_domain_watcher(void) +{ + int qemu_running = 1; + int fd[2], i, n, rc; + char byte; + + pipe(fd); + if (fork() != 0) + return; /* not child */ + + /* close all file handles, except stdio/out/err, + * our watch pipe and the xen interface handle */ + n = getdtablesize(); + for (i = 3; i < n; i++) { + if (i == fd[0]) + continue; + if (i == xen_xc) + continue; + close(i); + } + + /* ignore term signals */ + signal(SIGINT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + + /* wait for qemu exiting */ + while (qemu_running) { + rc = read(fd[0], &byte, 1); + switch (rc) { + case -1: + if (EINTR == errno) + continue; + qemu_log("%s: Huh? read error: %s\n", __FUNCTION__, strerror(errno)); + qemu_running = 0; + break; + case 0: + /* EOF -> qemu exited */ + qemu_running = 0; + break; + default: + qemu_log("%s: Huh? data on the watch pipe?\n", __FUNCTION__); + break; + } + } + + /* cleanup */ + qemu_log("%s: destroy domain %d\n", __FUNCTION__, xen_domid); + xc_domain_destroy(xen_xc, xen_domid); + _exit(0); +} + +/* normal cleanup */ +static void xen_domain_cleanup(void) +{ + char *dom; + + dom = xs_get_domain_path(xenstore, xen_domid); + if (dom) { + xs_rm(xenstore, 0, dom); + free(dom); + } + xs_release_domain(xenstore, xen_domid); +} + +int xen_domain_build_pv(const char *kernel, const char *ramdisk, + const char *cmdline) +{ + uint32_t ssidref = 0; + uint32_t flags = 0; + xen_domain_handle_t uuid; + unsigned int xenstore_port = 0, console_port = 0; + unsigned long xenstore_mfn = 0, console_mfn = 0; + int rc; + + memcpy(uuid, qemu_uuid, sizeof(uuid)); + rc = xc_domain_create(xen_xc, ssidref, uuid, flags, &xen_domid); + if (rc < 0) { + fprintf(stderr, "xen: xc_domain_create() failed\n"); + goto err; + } + qemu_log("xen: created domain %d\n", xen_domid); + atexit(xen_domain_cleanup); + xen_domain_watcher(); + + xenstore_domain_init1(kernel, ramdisk, cmdline); + + rc = xc_domain_max_vcpus(xen_xc, xen_domid, smp_cpus); + if (rc < 0) { + fprintf(stderr, "xen: xc_domain_max_vcpus() failed\n"); + goto err; + } + +#if 0 + rc = xc_domain_setcpuweight(xen_xc, xen_domid, 256); + if (rc < 0) { + fprintf(stderr, "xen: xc_domain_setcpuweight() failed\n"); + goto err; + } +#endif + + rc = xc_domain_setmaxmem(xen_xc, xen_domid, ram_size >> 10); + if (rc < 0) { + fprintf(stderr, "xen: xc_domain_setmaxmem() failed\n"); + goto err; + } + + xenstore_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); + console_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); + + rc = xc_linux_build(xen_xc, xen_domid, ram_size >> 20, + kernel, ramdisk, cmdline, + 0, flags, + xenstore_port, &xenstore_mfn, + console_port, &console_mfn); + if (rc < 0) { + fprintf(stderr, "xen: xc_linux_build() failed\n"); + goto err; + } + + xenstore_domain_init2(xenstore_port, xenstore_mfn, + console_port, console_mfn); + + qemu_log("xen: unpausing domain %d\n", xen_domid); + rc = xc_domain_unpause(xen_xc, xen_domid); + if (rc < 0) { + fprintf(stderr, "xen: xc_domain_unpause() failed\n"); + goto err; + } + + xen_poll = qemu_new_timer(rt_clock, xen_domain_poll, NULL); + qemu_mod_timer(xen_poll, qemu_get_clock(rt_clock) + 1000); + return 0; + +err: + return -1; +} diff --git a/hw/xen_domainbuild.h b/hw/xen_domainbuild.h new file mode 100644 index 0000000..dea0121 --- /dev/null +++ b/hw/xen_domainbuild.h @@ -0,0 +1,13 @@ +#ifndef QEMU_HW_XEN_DOMAINBUILD_H +#define QEMU_HW_XEN_DOMAINBUILD_H 1 + +#include "xen_common.h" + +int xenstore_domain_init1(const char *kernel, const char *ramdisk, + const char *cmdline); +int xenstore_domain_init2(int xenstore_port, int xenstore_mfn, + int console_port, int console_mfn); +int xen_domain_build_pv(const char *kernel, const char *ramdisk, + const char *cmdline); + +#endif /* QEMU_HW_XEN_DOMAINBUILD_H */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 154cf71..56b01aa 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -27,6 +27,7 @@ #include "sysemu.h" #include "boards.h" #include "xen_backend.h" +#include "xen_domainbuild.h" uint32_t xen_domid; enum xen_mode xen_mode = XEN_EMULATE; @@ -57,6 +58,24 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); exit(1); } + + switch (xen_mode) { + case XEN_ATTACH: + /* nothing to do, xend handles everything */ + break; + case XEN_CREATE: + if (xen_domain_build_pv(kernel_filename, initrd_filename, + kernel_cmdline) < 0) { + fprintf(stderr, "xen pv domain creation failed\n"); + exit(1); + } + break; + case XEN_EMULATE: + fprintf(stderr, "xen emulation not implemented (yet)\n"); + exit(1); + break; + } + xen_be_register("console", &xen_console_ops); xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- vl.c | 15 ++++----------- 1 files changed, 4 insertions(+), 11 deletions(-) diff --git a/vl.c b/vl.c index 19cd28f..7d6dcb6 100644 --- a/vl.c +++ b/vl.c @@ -4162,23 +4162,16 @@ static void select_vgahw (const char *p) { const char *opts; + cirrus_vga_enabled = 0; + std_vga_enabled = 0; + vmsvga_enabled = 0; if (strstart(p, "std", &opts)) { std_vga_enabled = 1; - cirrus_vga_enabled = 0; - vmsvga_enabled = 0; } else if (strstart(p, "cirrus", &opts)) { cirrus_vga_enabled = 1; - std_vga_enabled = 0; - vmsvga_enabled = 0; } else if (strstart(p, "vmware", &opts)) { - cirrus_vga_enabled = 0; - std_vga_enabled = 0; vmsvga_enabled = 1; - } else if (strstart(p, "none", &opts)) { - cirrus_vga_enabled = 0; - std_vga_enabled = 0; - vmsvga_enabled = 0; - } else { + } else if (!strstart(p, "none", &opts)) { invalid_vga: fprintf(stderr, "Unknown vga type: %s\n", p); exit(1); -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-21 12:20 UTC
[Xen-devel] [PATCH 10/10] xen: add -vga xenfb option, configure xenfb
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- hw/xen_machine_pv.c | 6 ++++++ qemu-options.hx | 2 +- sysemu.h | 1 + vl.c | 4 ++++ 4 files changed, 12 insertions(+), 1 deletions(-) diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 56b01aa..58209b8 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -82,6 +82,12 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, xen_be_register("qdisk", &xen_blkdev_ops); xen_be_register("qnic", &xen_netdev_ops); + /* configure framebuffer */ + if (xenfb_enabled) { + xen_config_dev_vfb(0, "vnc"); + xen_config_dev_vkbd(0); + } + /* configure disks */ for (i = 0; i < 16; i++) { index = drive_get_index(IF_XEN, 0, i); diff --git a/qemu-options.hx b/qemu-options.hx index 1f78021..7ae535a 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -457,7 +457,7 @@ Rotate graphical output 90 deg left (only PXA LCD). ETEXI DEF("vga", HAS_ARG, QEMU_OPTION_vga, - "-vga [std|cirrus|vmware|none]\n" + "-vga [std|cirrus|vmware|xenfb|none]\n" " select video card type\n") STEXI @item -vga @var{type} diff --git a/sysemu.h b/sysemu.h index 1f1a280..d3f1cb6 100644 --- a/sysemu.h +++ b/sysemu.h @@ -88,6 +88,7 @@ extern int bios_size; extern int cirrus_vga_enabled; extern int std_vga_enabled; extern int vmsvga_enabled; +extern int xenfb_enabled; extern int graphic_width; extern int graphic_height; extern int graphic_depth; diff --git a/vl.c b/vl.c index 7d6dcb6..1b34662 100644 --- a/vl.c +++ b/vl.c @@ -216,6 +216,7 @@ static int rtc_date_offset = -1; /* -1 means no change */ int cirrus_vga_enabled = 1; int std_vga_enabled = 0; int vmsvga_enabled = 0; +int xenfb_enabled = 0; #ifdef TARGET_SPARC int graphic_width = 1024; int graphic_height = 768; @@ -4165,12 +4166,15 @@ static void select_vgahw (const char *p) cirrus_vga_enabled = 0; std_vga_enabled = 0; vmsvga_enabled = 0; + xenfb_enabled = 0; if (strstart(p, "std", &opts)) { std_vga_enabled = 1; } else if (strstart(p, "cirrus", &opts)) { cirrus_vga_enabled = 1; } else if (strstart(p, "vmware", &opts)) { vmsvga_enabled = 1; + } else if (strstart(p, "xenfb", &opts)) { + xenfb_enabled = 1; } else if (!strstart(p, "none", &opts)) { invalid_vga: fprintf(stderr, "Unknown vga type: %s\n", p); -- 1.6.2.2 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann writes ("[Xen-devel] [PATCH 00/10] xen: pv domain support."):> Next round, addressing review comments:...> Ian, is just pulling from git fine with you? Or do you want the > qemu-xen patches mailed to xen-devel?I think that for form''s sake it would be good for you to mail the patches to xen-devel but I''m happy to pull. So ideally a patch series submission which says `here are the diffs 0/n and also a git URL''. Is that a lot of work for you ? If so I can probably produce such a series of emails myself but I think since you''re the originator of these patches I think it would make more sense for you to do it. Thanks for your contributions. I''m definitely keen to have them and I''ll be branching qemu-xen so that I can take your changes now rather than waiting for the Xen 3.4 release freeze to end. It''s good that you''re posting this code to qemu-devel and getting review there. But I''d like the qemu developers to hold off committing these changes to their tree until the files which are being added are 100% identical in Gerd''s patch series to in qemu-xen-unstable. That way qemu upstream will remain directly mergeable into qemu-xen-unstable because the code in the new files will be identical. Anything else will make my life very painful. To make this happen I''m of course happy to take patches to make sure that the code which is in qemu-xen-unstable is in the form that qemu upstream want it for merging. Ian. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
On 04/21/09 17:25, Ian Jackson wrote:> Gerd Hoffmann writes ("[Xen-devel] [PATCH 00/10] xen: pv domain support."): >> Next round, addressing review comments: > ... >> Ian, is just pulling from git fine with you? Or do you want the >> qemu-xen patches mailed to xen-devel? > > I think that for form''s sake it would be good for you to mail the > patches to xen-devel but I''m happy to pull. So ideally a patch series > submission which says `here are the diffs 0/n and also a git URL''. > Is that a lot of work for you ?No, will do, git does most of the hard work ;) I''m just asking because it is a bit redundant and I don''t want to patch-flood the lists more than necessary ... cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2009-Apr-22 15:52 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support.
Gerd Hoffmann wrote:> Hi, > > Next round, addressing review comments: > > * Fixup more "if (-1 == foo()) bar()" compare style. > * Fixup logging: kill fprintf(stderr, ...), use logging API. > * Kill some unneeded malloc failure checks. > > The patches are also rebased to latest git and got a few adaptions to > recent cha >Applied full series. Thanks. I think it can still use some love (some files missing copyrights/license, for instance), but it''s all extremely well contained so there''s little point in keeping it out of tree. Regards, Anthony Liguori> cheers, > Gerd > > > >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Stefano Stabellini
2009-Apr-22 16:54 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support.
Anthony Liguori wrote:> Gerd Hoffmann wrote: >> Hi, >> >> Next round, addressing review comments: >> >> * Fixup more "if (-1 == foo()) bar()" compare style. >> * Fixup logging: kill fprintf(stderr, ...), use logging API. >> * Kill some unneeded malloc failure checks. >> >> The patches are also rebased to latest git and got a few adaptions to >> recent cha >> > > Applied full series. Thanks. > > I think it can still use some love (some files missing > copyrights/license, for instance), but it''s all extremely well contained > so there''s little point in keeping it out of tree. >The point was for Ian to be able to pull directly from qemu without getting any conflicts on those files. To be honest they are fairly similar now, so I am confident that git will be able to do its magic. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2009-Apr-23 09:55 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 00/10] xen: pv domain support.
Hi,> The point was for Ian to be able to pull directly from qemu without > getting any conflicts on those files. > To be honest they are fairly similar now, so I am confident that git > will be able to do its magic.Ian can merge the changes from my repo first, then upstream qemu. Maybe the later splitted into the first four patches (the stuff already in qemu-xen) and then the remaining bits (disk+nic backend & friends) which Ian wants to get via upstream qemu. That should work out well. cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Hi Ian, On Tue, Apr 21, 2009 at 11:25 AM, Ian Jackson <Ian.Jackson@eu.citrix.com> wrote:> Gerd Hoffmann writes ("[Xen-devel] [PATCH 00/10] xen: pv domain support."): >> Next round, addressing review comments: > ... >> Ian, is just pulling from git fine with you? Or do you want the >> qemu-xen patches mailed to xen-devel? > > I think that for form''s sake it would be good for you to mail the > patches to xen-devel but I''m happy to pull. So ideally a patch series > submission which says `here are the diffs 0/n and also a git URL''. > Is that a lot of work for you ? If so I can probably produce such a > series of emails myself but I think since you''re the originator of > these patches I think it would make more sense for you to do it. > > Thanks for your contributions. I''m definitely keen to have them and > I''ll be branching qemu-xen so that I can take your changes now rather > than waiting for the Xen 3.4 release freeze to end.Do you have an idea when you expect to push these to qemu-unstable? Thanks, MRJ> It''s good that you''re posting this code to qemu-devel and getting > review there. But I''d like the qemu developers to hold off committing > these changes to their tree until the files which are being added are > 100% identical in Gerd''s patch series to in qemu-xen-unstable. > > That way qemu upstream will remain directly mergeable into > qemu-xen-unstable because the code in the new files will be identical. > Anything else will make my life very painful. > > To make this happen I''m of course happy to take patches to make sure > that the code which is in qemu-xen-unstable is in the form that qemu > upstream want it for merging. > > Ian. > > _______________________________________________ > Xen-devel mailing list > Xen-devel@lists.xensource.com > http://lists.xensource.com/xen-devel >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Mark Johnson writes ("Re: [Xen-devel] [PATCH 00/10] xen: pv domain support."):> Do you have an idea when you expect to push these to qemu-unstable?I pushed them to staging earlier today. Hopefully they''ll make it through our automated tests without problems. Thanks, Ian. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel