All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
@ 2008-07-28 13:17 ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

  Hi folks,

Here are a bunch of patches which start adding xen support to qemu.
Overview (individual patches have longer descriptions):

  #1 -- groundwork: build system, cmd line options, ...
  #2 -- xen backend driver infrastructrure
  #3 -- xen console backend driver
  #4 -- xen framebuffer backend driver
  #5 -- xen block backend driver
  #6 -- xen nic backend driver
  #7 -- allow xen disks and nics being configured via qemu command
        line options.

With the first four patches in place upstream qemu can replace xen's
qemu-dm for paravirtual domains.  The block and nic backend drivers are
full userspace implementations using the grant table device (gntdev).

xen support is implemented using another machine type.  xen's qemu-dm
already uses the machine type to switch between paravirtualized and
fully virtualized machines, so this was the natural choice.  qemu has
gets a new "xenpv" machine type additionally to the "pc" and "isapc"
ones.

I've placed the new files into the hw/ directory.  With all my xen bits
(partly not submitted yet with these patches) those add up to 18 new
files, all prefixed with xen.  Hmm, maybe I should better place them
somewhere else, so the already quite crowded hw/ directory doesn't
become even more cluttered?  If so, any suggestions?  New toplevel
directory?  New subdirectory below hw/?  Something else?

Comments?

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCH 0/7] merge some xen bits into qemu
@ 2008-07-28 13:17 ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

  Hi folks,

Here are a bunch of patches which start adding xen support to qemu.
Overview (individual patches have longer descriptions):

  #1 -- groundwork: build system, cmd line options, ...
  #2 -- xen backend driver infrastructrure
  #3 -- xen console backend driver
  #4 -- xen framebuffer backend driver
  #5 -- xen block backend driver
  #6 -- xen nic backend driver
  #7 -- allow xen disks and nics being configured via qemu command
        line options.

With the first four patches in place upstream qemu can replace xen's
qemu-dm for paravirtual domains.  The block and nic backend drivers are
full userspace implementations using the grant table device (gntdev).

xen support is implemented using another machine type.  xen's qemu-dm
already uses the machine type to switch between paravirtualized and
fully virtualized machines, so this was the natural choice.  qemu has
gets a new "xenpv" machine type additionally to the "pc" and "isapc"
ones.

I've placed the new files into the hw/ directory.  With all my xen bits
(partly not submitted yet with these patches) those add up to 18 new
files, all prefixed with xen.  Hmm, maybe I should better place them
somewhere else, so the already quite crowded hw/ directory doesn't
become even more cluttered?  If so, any suggestions?  New toplevel
directory?  New subdirectory below hw/?  Something else?

Comments?

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 65+ messages in thread

* [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
  2008-07-28 13:17 ` Gerd Hoffmann
@ 2008-07-28 13:17   ` Gerd Hoffmann
  -1 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

- configure script and build system changes.
- wind up new machine type.
- add -domid command line option.
- allow xenpv machines run without disk and kernel specified.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 Makefile.target       |    8 +++++
 configure             |   27 ++++++++++++++++
 hw/boards.h           |    4 ++
 hw/xen-machine.c      |   83 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/xen.h              |   12 +++++++
 target-i386/machine.c |    3 ++
 vl.c                  |   25 +++++++++++++--
 7 files changed, 159 insertions(+), 3 deletions(-)
 create mode 100644 hw/xen-machine.c
 create mode 100644 hw/xen.h

diff --git a/Makefile.target b/Makefile.target
index ff105c1..4f42582 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -515,6 +515,14 @@ CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
 LIBS += $(CONFIG_VNC_TLS_LIBS)
 endif
 
+# xen backend driver support
+XEN_OBJS := xen-machine.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 fd04766..34516b9 100755
--- a/configure
+++ b/configure
@@ -108,6 +108,7 @@ uname_release=""
 curses="yes"
 nptl="yes"
 mixemu="no"
+xen="no"
 
 # OS specific
 targetos=`uname -s`
@@ -202,6 +203,7 @@ linux="yes"
 linux_user="yes"
 if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
     kqemu="yes"
+    xen="yes"
     audio_possible_drivers="$audio_possible_drivers fmod"
 fi
 ;;
@@ -285,6 +287,8 @@ for opt do
   ;;
   --disable-kqemu) kqemu="no"
   ;;
+  --disable-xen) xen="no"
+  ;;
   --disable-brlapi) brlapi="no"
   ;;
   --enable-profiler) profiler="yes"
@@ -421,6 +425,7 @@ echo "                           Available drivers: $audio_possible_drivers"
 echo "  --audio-card-list=LIST   set list of additional emulated audio cards"
 echo "                           Available cards: ac97 adlib cs4231a gus"
 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-curses         disable curses output"
@@ -681,6 +686,22 @@ else
 fi
 
 ##########################################
+# xen probe
+
+if test "$xen" = "yes" ; then
+cat > $TMPC <<EOF
+#include <xs.h>
+#include <xenctrl.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
@@ -916,6 +937,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" ] && \
@@ -1167,6 +1189,11 @@ if test "$brlapi" = "yes" ; then
   echo "#define CONFIG_BRLAPI 1" >> $config_h
   echo "BRLAPI_LIBS=-lbrlapi" >> $config_mak
 fi
+if test "$xen" = "yes" ; then
+  echo "CONFIG_XEN=yes" >> $config_mak
+  echo "#define CONFIG_XEN 1" >> $config_h
+  echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak
+fi
 
 # XXX: suppress that
 if [ "$bsd" = "yes" ] ; then
diff --git a/hw/boards.h b/hw/boards.h
index 22ac332..5931720 100644
--- a/hw/boards.h
+++ b/hw/boards.h
@@ -29,6 +29,10 @@ extern QEMUMachine bareetraxfs_machine;
 extern QEMUMachine pc_machine;
 extern QEMUMachine isapc_machine;
 
+/* xen_machine.c */
+extern QEMUMachine xenpv_machine;
+extern QEMUMachine xenfv_machine;
+
 /* ppc.c */
 extern QEMUMachine prep_machine;
 extern QEMUMachine core99_machine;
diff --git a/hw/xen-machine.c b/hw/xen-machine.c
new file mode 100644
index 0000000..88f0f6e
--- /dev/null
+++ b/hw/xen-machine.c
@@ -0,0 +1,83 @@
+/*
+ * QEMU Xen PV Machine
+ *
+ * Copyright (c) 2007,08 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 "boards.h"
+
+#include "xen.h"
+
+/* -------------------------------------------------------------------- */
+/* variables                                                            */
+
+int xen_domid;
+
+/* -------------------------------------------------------------------- */
+/* paravirtualized xen guests                                           */
+
+static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
+		       const char *boot_device, DisplayState *ds,
+		       const char *kernel_filename,
+		       const char *kernel_cmdline,
+		       const char *initrd_filename,
+		       const char *cpu_model)
+{
+    CPUState *env;
+
+    /* create dummy cpu, halted */
+    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 = {
+    "xenpv",
+    "paravirtualized Xen machine",
+    xenpv_init,
+};
+
+/* -------------------------------------------------------------------- */
+/* fully virtualized xen guests                                         */
+
+static void xenfv_init(ram_addr_t ram_size, int vga_ram_size,
+		       const char *boot_device, DisplayState *ds,
+		       const char *kernel_filename,
+		       const char *kernel_cmdline,
+		       const char *initrd_filename,
+		       const char *cpu_model)
+{
+    /* to be done */
+    fprintf(stderr, "%s: not implemented yet\n", __FUNCTION__);
+}
+
+QEMUMachine xenfv_machine = {
+    "xenfv",
+    "fully virtualized Xen machine",
+    xenfv_init,
+};
diff --git a/hw/xen.h b/hw/xen.h
new file mode 100644
index 0000000..19349b5
--- /dev/null
+++ b/hw/xen.h
@@ -0,0 +1,12 @@
+/*
+ * public xen header
+ *   stuff needed outside xen-*.c, i.e. interfaces to qemu.
+ *   should not depend on any xen headers being present in
+ *   /usr/include/xen, so it can be included unconditionally.
+ *
+ * internal bits for the xen backend drivers are in xen-backend.h
+ */
+
+/* xen-machine.c */
+extern int xen_domid;
+
diff --git a/target-i386/machine.c b/target-i386/machine.c
index 91dbd55..98ece17 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 8801615..8aef3bd 100644
--- a/vl.c
+++ b/vl.c
@@ -29,6 +29,7 @@
 #include "hw/audiodev.h"
 #include "hw/isa.h"
 #include "hw/baum.h"
+#include "hw/xen.h"
 #include "net.h"
 #include "console.h"
 #include "sysemu.h"
@@ -7737,6 +7738,9 @@ static void help(int exitcode)
            "-startdate      select initial date of the clock\n"
            "-icount [N|auto]\n"
            "                Enable virtual instruction counter with 2^N clock ticks per instruction\n"
+#ifdef CONFIG_XEN
+	   "-domid          specify xen guest domain id\n"
+#endif
            "\n"
            "During emulation, the following keys are useful:\n"
            "ctrl-alt-f      toggle full screen\n"
@@ -7842,6 +7846,9 @@ enum {
     QEMU_OPTION_startdate,
     QEMU_OPTION_tb_size,
     QEMU_OPTION_icount,
+#ifdef CONFIG_XEN
+    QEMU_OPTION_domid,
+#endif
 };
 
 typedef struct QEMUOption {
@@ -7930,6 +7937,9 @@ const QEMUOption qemu_options[] = {
 #ifdef CONFIG_CURSES
     { "curses", 0, QEMU_OPTION_curses },
 #endif
+#ifdef CONFIG_XEN
+    { "domid", HAS_ARG, QEMU_OPTION_domid },
+#endif
 
     /* temporary options */
     { "usb", 0, QEMU_OPTION_usb },
@@ -8150,7 +8160,7 @@ int main(int argc, char **argv)
 #endif
     uint32_t boot_devices_bitmap = 0;
     int i;
-    int snapshot, linux_boot, net_boot;
+    int snapshot, linux_boot, net_boot, nodisk_ok;
     const char *initrd_filename;
     const char *kernel_filename, *kernel_cmdline;
     const char *boot_devices = "";
@@ -8787,6 +8797,11 @@ int main(int argc, char **argv)
                     icount_time_shift = strtol(optarg, NULL, 0);
                 }
                 break;
+#ifdef CONFIG_XEN
+            case QEMU_OPTION_domid:
+                xen_domid = atoi(optarg);
+                break;
+#endif
             }
         }
     }
@@ -8852,9 +8867,13 @@ int main(int argc, char **argv)
     linux_boot = (kernel_filename != NULL);
     net_boot = (boot_devices_bitmap >> ('n' - 'a')) & 0xF;
 
-    /* XXX: this should not be: some embedded targets just have flash */
+    /* need a disk for this machine to boot ? */
+    /* XXX: add embedded targets which just have flash */
+    nodisk_ok = 0;
+    if (0 == strcmp(machine->name, "xenpv"))
+	nodisk_ok = 1;
     if (!linux_boot && net_boot == 0 &&
-        nb_drives_opt == 0)
+        !nodisk_ok && nb_drives_opt == 0)
         help(1);
 
     if (!linux_boot && *kernel_cmdline != '\0') {
-- 
1.5.4.1

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 1/7] xen: groundwork for xen support
@ 2008-07-28 13:17   ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

- configure script and build system changes.
- wind up new machine type.
- add -domid command line option.
- allow xenpv machines run without disk and kernel specified.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 Makefile.target       |    8 +++++
 configure             |   27 ++++++++++++++++
 hw/boards.h           |    4 ++
 hw/xen-machine.c      |   83 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/xen.h              |   12 +++++++
 target-i386/machine.c |    3 ++
 vl.c                  |   25 +++++++++++++--
 7 files changed, 159 insertions(+), 3 deletions(-)
 create mode 100644 hw/xen-machine.c
 create mode 100644 hw/xen.h

diff --git a/Makefile.target b/Makefile.target
index ff105c1..4f42582 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -515,6 +515,14 @@ CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
 LIBS += $(CONFIG_VNC_TLS_LIBS)
 endif
 
+# xen backend driver support
+XEN_OBJS := xen-machine.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 fd04766..34516b9 100755
--- a/configure
+++ b/configure
@@ -108,6 +108,7 @@ uname_release=""
 curses="yes"
 nptl="yes"
 mixemu="no"
+xen="no"
 
 # OS specific
 targetos=`uname -s`
@@ -202,6 +203,7 @@ linux="yes"
 linux_user="yes"
 if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
     kqemu="yes"
+    xen="yes"
     audio_possible_drivers="$audio_possible_drivers fmod"
 fi
 ;;
@@ -285,6 +287,8 @@ for opt do
   ;;
   --disable-kqemu) kqemu="no"
   ;;
+  --disable-xen) xen="no"
+  ;;
   --disable-brlapi) brlapi="no"
   ;;
   --enable-profiler) profiler="yes"
@@ -421,6 +425,7 @@ echo "                           Available drivers: $audio_possible_drivers"
 echo "  --audio-card-list=LIST   set list of additional emulated audio cards"
 echo "                           Available cards: ac97 adlib cs4231a gus"
 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-curses         disable curses output"
@@ -681,6 +686,22 @@ else
 fi
 
 ##########################################
+# xen probe
+
+if test "$xen" = "yes" ; then
+cat > $TMPC <<EOF
+#include <xs.h>
+#include <xenctrl.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
@@ -916,6 +937,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" ] && \
@@ -1167,6 +1189,11 @@ if test "$brlapi" = "yes" ; then
   echo "#define CONFIG_BRLAPI 1" >> $config_h
   echo "BRLAPI_LIBS=-lbrlapi" >> $config_mak
 fi
+if test "$xen" = "yes" ; then
+  echo "CONFIG_XEN=yes" >> $config_mak
+  echo "#define CONFIG_XEN 1" >> $config_h
+  echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak
+fi
 
 # XXX: suppress that
 if [ "$bsd" = "yes" ] ; then
diff --git a/hw/boards.h b/hw/boards.h
index 22ac332..5931720 100644
--- a/hw/boards.h
+++ b/hw/boards.h
@@ -29,6 +29,10 @@ extern QEMUMachine bareetraxfs_machine;
 extern QEMUMachine pc_machine;
 extern QEMUMachine isapc_machine;
 
+/* xen_machine.c */
+extern QEMUMachine xenpv_machine;
+extern QEMUMachine xenfv_machine;
+
 /* ppc.c */
 extern QEMUMachine prep_machine;
 extern QEMUMachine core99_machine;
diff --git a/hw/xen-machine.c b/hw/xen-machine.c
new file mode 100644
index 0000000..88f0f6e
--- /dev/null
+++ b/hw/xen-machine.c
@@ -0,0 +1,83 @@
+/*
+ * QEMU Xen PV Machine
+ *
+ * Copyright (c) 2007,08 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 "boards.h"
+
+#include "xen.h"
+
+/* -------------------------------------------------------------------- */
+/* variables                                                            */
+
+int xen_domid;
+
+/* -------------------------------------------------------------------- */
+/* paravirtualized xen guests                                           */
+
+static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
+		       const char *boot_device, DisplayState *ds,
+		       const char *kernel_filename,
+		       const char *kernel_cmdline,
+		       const char *initrd_filename,
+		       const char *cpu_model)
+{
+    CPUState *env;
+
+    /* create dummy cpu, halted */
+    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 = {
+    "xenpv",
+    "paravirtualized Xen machine",
+    xenpv_init,
+};
+
+/* -------------------------------------------------------------------- */
+/* fully virtualized xen guests                                         */
+
+static void xenfv_init(ram_addr_t ram_size, int vga_ram_size,
+		       const char *boot_device, DisplayState *ds,
+		       const char *kernel_filename,
+		       const char *kernel_cmdline,
+		       const char *initrd_filename,
+		       const char *cpu_model)
+{
+    /* to be done */
+    fprintf(stderr, "%s: not implemented yet\n", __FUNCTION__);
+}
+
+QEMUMachine xenfv_machine = {
+    "xenfv",
+    "fully virtualized Xen machine",
+    xenfv_init,
+};
diff --git a/hw/xen.h b/hw/xen.h
new file mode 100644
index 0000000..19349b5
--- /dev/null
+++ b/hw/xen.h
@@ -0,0 +1,12 @@
+/*
+ * public xen header
+ *   stuff needed outside xen-*.c, i.e. interfaces to qemu.
+ *   should not depend on any xen headers being present in
+ *   /usr/include/xen, so it can be included unconditionally.
+ *
+ * internal bits for the xen backend drivers are in xen-backend.h
+ */
+
+/* xen-machine.c */
+extern int xen_domid;
+
diff --git a/target-i386/machine.c b/target-i386/machine.c
index 91dbd55..98ece17 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 8801615..8aef3bd 100644
--- a/vl.c
+++ b/vl.c
@@ -29,6 +29,7 @@
 #include "hw/audiodev.h"
 #include "hw/isa.h"
 #include "hw/baum.h"
+#include "hw/xen.h"
 #include "net.h"
 #include "console.h"
 #include "sysemu.h"
@@ -7737,6 +7738,9 @@ static void help(int exitcode)
            "-startdate      select initial date of the clock\n"
            "-icount [N|auto]\n"
            "                Enable virtual instruction counter with 2^N clock ticks per instruction\n"
+#ifdef CONFIG_XEN
+	   "-domid          specify xen guest domain id\n"
+#endif
            "\n"
            "During emulation, the following keys are useful:\n"
            "ctrl-alt-f      toggle full screen\n"
@@ -7842,6 +7846,9 @@ enum {
     QEMU_OPTION_startdate,
     QEMU_OPTION_tb_size,
     QEMU_OPTION_icount,
+#ifdef CONFIG_XEN
+    QEMU_OPTION_domid,
+#endif
 };
 
 typedef struct QEMUOption {
@@ -7930,6 +7937,9 @@ const QEMUOption qemu_options[] = {
 #ifdef CONFIG_CURSES
     { "curses", 0, QEMU_OPTION_curses },
 #endif
+#ifdef CONFIG_XEN
+    { "domid", HAS_ARG, QEMU_OPTION_domid },
+#endif
 
     /* temporary options */
     { "usb", 0, QEMU_OPTION_usb },
@@ -8150,7 +8160,7 @@ int main(int argc, char **argv)
 #endif
     uint32_t boot_devices_bitmap = 0;
     int i;
-    int snapshot, linux_boot, net_boot;
+    int snapshot, linux_boot, net_boot, nodisk_ok;
     const char *initrd_filename;
     const char *kernel_filename, *kernel_cmdline;
     const char *boot_devices = "";
@@ -8787,6 +8797,11 @@ int main(int argc, char **argv)
                     icount_time_shift = strtol(optarg, NULL, 0);
                 }
                 break;
+#ifdef CONFIG_XEN
+            case QEMU_OPTION_domid:
+                xen_domid = atoi(optarg);
+                break;
+#endif
             }
         }
     }
@@ -8852,9 +8867,13 @@ int main(int argc, char **argv)
     linux_boot = (kernel_filename != NULL);
     net_boot = (boot_devices_bitmap >> ('n' - 'a')) & 0xF;
 
-    /* XXX: this should not be: some embedded targets just have flash */
+    /* need a disk for this machine to boot ? */
+    /* XXX: add embedded targets which just have flash */
+    nodisk_ok = 0;
+    if (0 == strcmp(machine->name, "xenpv"))
+	nodisk_ok = 1;
     if (!linux_boot && net_boot == 0 &&
-        nb_drives_opt == 0)
+        !nodisk_ok && nb_drives_opt == 0)
         help(1);
 
     if (!linux_boot && *kernel_cmdline != '\0') {
-- 
1.5.4.1

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Qemu-devel] [PATCH 2/7] xen: backend driver core
  2008-07-28 13:17 ` Gerd Hoffmann
@ 2008-07-28 13:17   ` Gerd Hoffmann
  -1 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

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/list.h        |  169 ++++++++++++++
 hw/xen-backend.c |  663 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/xen-backend.h |  116 ++++++++++
 hw/xen-machine.c |    5 +-
 5 files changed, 953 insertions(+), 2 deletions(-)
 create mode 100644 hw/list.h
 create mode 100644 hw/xen-backend.c
 create mode 100644 hw/xen-backend.h

diff --git a/Makefile.target b/Makefile.target
index 4f42582..0451048 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -516,7 +516,7 @@ LIBS += $(CONFIG_VNC_TLS_LIBS)
 endif
 
 # xen backend driver support
-XEN_OBJS := xen-machine.o
+XEN_OBJS := xen-machine.o xen-backend.o
 ifeq ($(CONFIG_XEN), yes)
   OBJS += $(XEN_OBJS)
   LIBS += $(XEN_LIBS)
diff --git a/hw/list.h b/hw/list.h
new file mode 100644
index 0000000..fa9f790
--- /dev/null
+++ b/hw/list.h
@@ -0,0 +1,169 @@
+#ifndef _LIST_H
+#define _LIST_H 1
+
+/*
+ * Simple doubly linked list implementation.
+ *	-- shamelessly stolen from the linux kernel sources
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a item entry between two known consecutive entries. 
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * item,
+	struct list_head * prev,
+	struct list_head * next)
+{
+	next->prev = item;
+	item->next = next;
+	item->prev = prev;
+	prev->next = item;
+}
+
+/**
+ * list_add - add a item entry
+ * @item: item entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a item entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static __inline__ void list_add(struct list_head *item, struct list_head *head)
+{
+	__list_add(item, head, head->next);
+}
+
+/**
+ * list_add_tail - add a item entry
+ * @item: item entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a item entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static __inline__ void list_add_tail(struct list_head *item, struct list_head *head)
+{
+	__list_add(item, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+				  struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
+ */
+static __inline__ void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static __inline__ void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry); 
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static __inline__ int list_empty(struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the item list to add.
+ * @head: the place to add it in the first list.
+ */
+static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+{
+	struct list_head *first = list->next;
+
+	if (first != list) {
+		struct list_head *last = list->prev;
+		struct list_head *at = head->next;
+
+		first->prev = head;
+		head->next = first;
+
+		last->next = at;
+		at->prev = last;
+	}
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+        	
+/**
+ * list_for_each_safe	-	iterate over a list safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev	-	iterate over a list in reverse order
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+	for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+#endif /* _LIST_H */
diff --git a/hw/xen-backend.c b/hw/xen-backend.c
new file mode 100644
index 0000000..4819624
--- /dev/null
+++ b/hw/xen-backend.c
@@ -0,0 +1,663 @@
+/*
+ *  xen backend driver infrastructure
+ *
+ *  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 <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 LIST_HEAD(xendevs);
+static const char *root = "/local/domain/0/backend";
+static int debug = 0;
+
+/* ------------------------------------------------------------- */
+
+int xenstore_write_str(const char *base, const char *node, const char *val)
+{
+    char abspath[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[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;
+    free(val);
+    return rc;
+}
+
+int xenstore_write_be_str(struct xendev *xendev, const char *node, const char *val)
+{
+    return xenstore_write_str(xendev->be, node, val);
+}
+
+int xenstore_write_be_int(struct xendev *xendev, const char *node, int ival)
+{
+    return xenstore_write_int(xendev->be, node, ival);
+}
+
+char *xenstore_read_be_str(struct xendev *xendev, const char *node)
+{
+    return xenstore_read_str(xendev->be, node);
+}
+
+int xenstore_read_be_int(struct xendev *xendev, const char *node, int *ival)
+{
+    return xenstore_read_int(xendev->be, node, ival);
+}
+
+char *xenstore_read_fe_str(struct xendev *xendev, const char *node)
+{
+    return xenstore_read_str(xendev->fe, node);
+}
+
+int xenstore_read_fe_int(struct xendev *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 xendev *xendev, enum xenbus_state state)
+{
+    int rc;
+
+    rc = xenstore_write_be_int(xendev, "state", state);
+    if (0 != rc)
+	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 xendev *xen_be_find_xendev(char *type, int dom, int dev)
+{
+    struct xendev *xendev;
+    struct list_head *item;
+
+    list_for_each(item, &xendevs) {
+	xendev = list_entry(item, struct xendev, 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 xendev *xen_be_get_xendev(char *type, int dom, int dev,
+					struct devops *ops)
+{
+    struct xendev *xendev;
+
+    xendev = xen_be_find_xendev(type, dom, dev);
+    if (xendev)
+	return xendev;
+
+    /* init new xendev */
+    xendev = malloc(ops->size);
+    memset(xendev,0,ops->size);
+    xendev->type  = type;
+    xendev->dom   = dom;
+    xendev->dev   = dev;
+    xendev->ops   = ops;
+    snprintf(xendev->be, sizeof(xendev->be), "%s/%s/%d/%d",
+	     root, xendev->type, xendev->dom, xendev->dev);
+    snprintf(xendev->name, sizeof(xendev->name), "%s-%d",
+	     xendev->type, xendev->dev);
+
+    xendev->debug      = debug;
+    xendev->local_port = -1;
+
+    xendev->evtchndev = xc_evtchn_open();
+    if (xendev->evtchndev < 0) {
+	fprintf(stderr, "can't open evtchn device\n");
+	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);
+	    free(xendev);
+	    return NULL;
+	}
+    } else {
+	xendev->gnttabdev = -1;
+    }
+
+    list_add_tail(&xendev->next, &xendevs);
+
+    if (xendev->ops->alloc)
+	xendev->ops->alloc(xendev);
+
+    return xendev;
+}
+
+/*
+ * release xen backend device.
+ */
+static struct xendev *xen_be_del_xendev(int dom, int dev)
+{
+    struct xendev *xendev;
+    struct list_head *item, *tmp;
+
+    list_for_each_safe(item, tmp, &xendevs) {
+	xendev = list_entry(item, struct xendev, 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[BUFSIZE];
+	    snprintf(token, sizeof(token), "fe:%p", xendev);
+	    xs_unwatch(xenstore, xendev->fe, token);
+	    free(xendev->fe);
+	}
+
+	if (xendev->evtchndev >= 0)
+	    xc_evtchn_close(xendev->evtchndev);
+	if (xendev->gnttabdev >= 0)
+	    xc_gnttab_close(xendev->gnttabdev);
+
+	list_del(&xendev->next);
+	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 xendev *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 xendev *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")) {
+	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 xendev *xendev)
+{
+    char token[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 xendev *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 xendev *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);
+
+#if 0
+    if (xendev->ops->event)
+	xendev->ops->event(xendev);
+#endif
+    return 0;
+}
+
+/*
+ * Teardown connection.
+ *
+ * Goes to Closed when done.
+ */
+static void xen_be_disconnect(struct xendev *xendev)
+{
+    if (xendev->be_state == XenbusStateClosed)
+	return;
+    if (xendev->ops->disconnect)
+	xendev->ops->disconnect(xendev);
+    xen_be_set_state(xendev, XenbusStateClosed);
+}
+
+/*
+ * state change dispatcher function
+ */
+void xen_be_check_state(struct xendev *xendev)
+{
+    int rc = 0;
+
+    /* frontend may request shutdown from almost anywhere */
+    if (xendev->fe_state == XenbusStateClosing ||
+	xendev->fe_state == XenbusStateClosed) {
+	xen_be_disconnect(xendev);
+	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;
+	default:
+	    rc = -1;
+	}
+	if (0 != rc)
+	    break;
+    }
+}
+
+/* ------------------------------------------------------------- */
+
+static int xenstore_scan(char *type, int dom, struct devops *ops)
+{
+    struct xendev *xendev;
+    char path[BUFSIZE], token[BUFSIZE];
+    char **dev = NULL;
+    unsigned int cdev, j;
+
+    /* setup watch */
+    snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops);
+    snprintf(path, sizeof(path), "%s/%s/%d", root, type, dom);
+    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);
+    }
+    free(dev);
+    return 0;
+}
+
+static void xenstore_update_be(char *watch, char *type, int dom,
+			       struct devops *ops)
+{
+    struct xendev *xendev;
+    char path[BUFSIZE];
+    unsigned int len, dev;
+
+    len = snprintf(path, sizeof(path), "%s/%s/%d", root, type, dom);
+    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 xendev *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:
+    free(vec);
+}
+
+static void xen_be_evtchn_event(void *opaque)
+{
+    struct xendev *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(char *type, struct devops *ops)
+{
+    return xenstore_scan(type, xen_domid, ops);
+}
+
+int xen_be_bind_evtchn(struct xendev *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) {
+	xendev->local_port = 0;
+	return -1;
+    }
+    qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev),
+			xen_be_evtchn_event, NULL, xendev);
+#if 0
+    xc_evtchn_unmask(xendev->evtchndev, xendev->local_port);
+#endif
+    return 0;
+}
+
+void xen_be_unbind_evtchn(struct xendev *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);
+    xendev->local_port = -1;
+}
+
+int xen_be_send_notify(struct xendev *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 xendev *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..db36ae7
--- /dev/null
+++ b/hw/xen-backend.h
@@ -0,0 +1,116 @@
+#include <stddef.h>
+#include <xs.h>
+#include <xenctrl.h>
+#include <xen/elfnote.h>
+#include <xen/elfstructs.h>
+
+#include <xen/io/xenbus.h>
+
+#include "list.h"
+
+#include "hw.h"
+#include "xen.h"
+
+/*
+ * 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__ < 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
+
+/* ------------------------------------------------------------- */
+
+#define container_of(ptr, type, member) ({                      \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/* ------------------------------------------------------------- */
+
+#define BUFSIZE 1024
+
+struct xendev;
+
+/* 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 devops {
+    size_t    size;
+    uint32_t  flags;
+    void      (*alloc)(struct xendev *xendev);
+    int       (*init)(struct xendev *xendev);
+    int       (*connect)(struct xendev *xendev);
+    void      (*event)(struct xendev *xendev);
+    void      (*disconnect)(struct xendev *xendev);
+    int       (*free)(struct xendev *xendev);
+    void      (*backend_changed)(struct xendev *xendev, const char *node);
+    void      (*frontend_changed)(struct xendev *xendev, const char *node);
+};
+
+struct xendev {
+    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[BUFSIZE];
+    char               *fe;
+    char               *protocol;
+    int                remote_port;
+    int                local_port;
+
+    int                evtchndev;
+    int                gnttabdev;
+
+    struct devops      *ops;
+    struct list_head   next;
+};
+
+/* ------------------------------------------------------------- */
+
+/* variables */
+extern int xen_domid; /* set by cmd line option, in vl.c */
+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 xendev *xendev, const char *node, const char *val);
+int xenstore_write_be_int(struct xendev *xendev, const char *node, int ival);
+char *xenstore_read_be_str(struct xendev *xendev, const char *node);
+int xenstore_read_be_int(struct xendev *xendev, const char *node, int *ival);
+char *xenstore_read_fe_str(struct xendev *xendev, const char *node);
+int xenstore_read_fe_int(struct xendev *xendev, const char *node, int *ival);
+
+const char *xenbus_strstate(enum xenbus_state state);
+struct xendev *xen_be_find_xendev(char *type, int dom, int dev);
+void xen_be_check_state(struct xendev *xendev);
+
+/* xen backend driver bits */
+int xen_be_init(void);
+int xen_be_register(char *type, struct devops *ops);
+int xen_be_set_state(struct xendev *xendev, enum xenbus_state state);
+int xen_be_bind_evtchn(struct xendev *xendev);
+void xen_be_unbind_evtchn(struct xendev *xendev);
+int xen_be_send_notify(struct xendev *xendev);
+void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...)
+    __attribute__ ((format(printf, 3, 4)));
+
diff --git a/hw/xen-machine.c b/hw/xen-machine.c
index 88f0f6e..798c0a7 100644
--- a/hw/xen-machine.c
+++ b/hw/xen-machine.c
@@ -25,7 +25,7 @@
 #include "hw.h"
 #include "boards.h"
 
-#include "xen.h"
+#include "xen-backend.h"
 
 /* -------------------------------------------------------------------- */
 /* variables                                                            */
@@ -54,6 +54,9 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
     }
     env = cpu_init(cpu_model);
     env->halted = 1;
+
+    /* setup xen backend handlers */
+    xen_be_init();
 }
 
 QEMUMachine xenpv_machine = {
-- 
1.5.4.1

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 2/7] xen: backend driver core
@ 2008-07-28 13:17   ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

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/list.h        |  169 ++++++++++++++
 hw/xen-backend.c |  663 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/xen-backend.h |  116 ++++++++++
 hw/xen-machine.c |    5 +-
 5 files changed, 953 insertions(+), 2 deletions(-)
 create mode 100644 hw/list.h
 create mode 100644 hw/xen-backend.c
 create mode 100644 hw/xen-backend.h

diff --git a/Makefile.target b/Makefile.target
index 4f42582..0451048 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -516,7 +516,7 @@ LIBS += $(CONFIG_VNC_TLS_LIBS)
 endif
 
 # xen backend driver support
-XEN_OBJS := xen-machine.o
+XEN_OBJS := xen-machine.o xen-backend.o
 ifeq ($(CONFIG_XEN), yes)
   OBJS += $(XEN_OBJS)
   LIBS += $(XEN_LIBS)
diff --git a/hw/list.h b/hw/list.h
new file mode 100644
index 0000000..fa9f790
--- /dev/null
+++ b/hw/list.h
@@ -0,0 +1,169 @@
+#ifndef _LIST_H
+#define _LIST_H 1
+
+/*
+ * Simple doubly linked list implementation.
+ *	-- shamelessly stolen from the linux kernel sources
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a item entry between two known consecutive entries. 
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * item,
+	struct list_head * prev,
+	struct list_head * next)
+{
+	next->prev = item;
+	item->next = next;
+	item->prev = prev;
+	prev->next = item;
+}
+
+/**
+ * list_add - add a item entry
+ * @item: item entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a item entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static __inline__ void list_add(struct list_head *item, struct list_head *head)
+{
+	__list_add(item, head, head->next);
+}
+
+/**
+ * list_add_tail - add a item entry
+ * @item: item entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a item entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static __inline__ void list_add_tail(struct list_head *item, struct list_head *head)
+{
+	__list_add(item, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+				  struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
+ */
+static __inline__ void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static __inline__ void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry); 
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static __inline__ int list_empty(struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the item list to add.
+ * @head: the place to add it in the first list.
+ */
+static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+{
+	struct list_head *first = list->next;
+
+	if (first != list) {
+		struct list_head *last = list->prev;
+		struct list_head *at = head->next;
+
+		first->prev = head;
+		head->next = first;
+
+		last->next = at;
+		at->prev = last;
+	}
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+        	
+/**
+ * list_for_each_safe	-	iterate over a list safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev	-	iterate over a list in reverse order
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+	for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+#endif /* _LIST_H */
diff --git a/hw/xen-backend.c b/hw/xen-backend.c
new file mode 100644
index 0000000..4819624
--- /dev/null
+++ b/hw/xen-backend.c
@@ -0,0 +1,663 @@
+/*
+ *  xen backend driver infrastructure
+ *
+ *  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 <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 LIST_HEAD(xendevs);
+static const char *root = "/local/domain/0/backend";
+static int debug = 0;
+
+/* ------------------------------------------------------------- */
+
+int xenstore_write_str(const char *base, const char *node, const char *val)
+{
+    char abspath[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[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;
+    free(val);
+    return rc;
+}
+
+int xenstore_write_be_str(struct xendev *xendev, const char *node, const char *val)
+{
+    return xenstore_write_str(xendev->be, node, val);
+}
+
+int xenstore_write_be_int(struct xendev *xendev, const char *node, int ival)
+{
+    return xenstore_write_int(xendev->be, node, ival);
+}
+
+char *xenstore_read_be_str(struct xendev *xendev, const char *node)
+{
+    return xenstore_read_str(xendev->be, node);
+}
+
+int xenstore_read_be_int(struct xendev *xendev, const char *node, int *ival)
+{
+    return xenstore_read_int(xendev->be, node, ival);
+}
+
+char *xenstore_read_fe_str(struct xendev *xendev, const char *node)
+{
+    return xenstore_read_str(xendev->fe, node);
+}
+
+int xenstore_read_fe_int(struct xendev *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 xendev *xendev, enum xenbus_state state)
+{
+    int rc;
+
+    rc = xenstore_write_be_int(xendev, "state", state);
+    if (0 != rc)
+	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 xendev *xen_be_find_xendev(char *type, int dom, int dev)
+{
+    struct xendev *xendev;
+    struct list_head *item;
+
+    list_for_each(item, &xendevs) {
+	xendev = list_entry(item, struct xendev, 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 xendev *xen_be_get_xendev(char *type, int dom, int dev,
+					struct devops *ops)
+{
+    struct xendev *xendev;
+
+    xendev = xen_be_find_xendev(type, dom, dev);
+    if (xendev)
+	return xendev;
+
+    /* init new xendev */
+    xendev = malloc(ops->size);
+    memset(xendev,0,ops->size);
+    xendev->type  = type;
+    xendev->dom   = dom;
+    xendev->dev   = dev;
+    xendev->ops   = ops;
+    snprintf(xendev->be, sizeof(xendev->be), "%s/%s/%d/%d",
+	     root, xendev->type, xendev->dom, xendev->dev);
+    snprintf(xendev->name, sizeof(xendev->name), "%s-%d",
+	     xendev->type, xendev->dev);
+
+    xendev->debug      = debug;
+    xendev->local_port = -1;
+
+    xendev->evtchndev = xc_evtchn_open();
+    if (xendev->evtchndev < 0) {
+	fprintf(stderr, "can't open evtchn device\n");
+	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);
+	    free(xendev);
+	    return NULL;
+	}
+    } else {
+	xendev->gnttabdev = -1;
+    }
+
+    list_add_tail(&xendev->next, &xendevs);
+
+    if (xendev->ops->alloc)
+	xendev->ops->alloc(xendev);
+
+    return xendev;
+}
+
+/*
+ * release xen backend device.
+ */
+static struct xendev *xen_be_del_xendev(int dom, int dev)
+{
+    struct xendev *xendev;
+    struct list_head *item, *tmp;
+
+    list_for_each_safe(item, tmp, &xendevs) {
+	xendev = list_entry(item, struct xendev, 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[BUFSIZE];
+	    snprintf(token, sizeof(token), "fe:%p", xendev);
+	    xs_unwatch(xenstore, xendev->fe, token);
+	    free(xendev->fe);
+	}
+
+	if (xendev->evtchndev >= 0)
+	    xc_evtchn_close(xendev->evtchndev);
+	if (xendev->gnttabdev >= 0)
+	    xc_gnttab_close(xendev->gnttabdev);
+
+	list_del(&xendev->next);
+	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 xendev *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 xendev *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")) {
+	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 xendev *xendev)
+{
+    char token[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 xendev *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 xendev *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);
+
+#if 0
+    if (xendev->ops->event)
+	xendev->ops->event(xendev);
+#endif
+    return 0;
+}
+
+/*
+ * Teardown connection.
+ *
+ * Goes to Closed when done.
+ */
+static void xen_be_disconnect(struct xendev *xendev)
+{
+    if (xendev->be_state == XenbusStateClosed)
+	return;
+    if (xendev->ops->disconnect)
+	xendev->ops->disconnect(xendev);
+    xen_be_set_state(xendev, XenbusStateClosed);
+}
+
+/*
+ * state change dispatcher function
+ */
+void xen_be_check_state(struct xendev *xendev)
+{
+    int rc = 0;
+
+    /* frontend may request shutdown from almost anywhere */
+    if (xendev->fe_state == XenbusStateClosing ||
+	xendev->fe_state == XenbusStateClosed) {
+	xen_be_disconnect(xendev);
+	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;
+	default:
+	    rc = -1;
+	}
+	if (0 != rc)
+	    break;
+    }
+}
+
+/* ------------------------------------------------------------- */
+
+static int xenstore_scan(char *type, int dom, struct devops *ops)
+{
+    struct xendev *xendev;
+    char path[BUFSIZE], token[BUFSIZE];
+    char **dev = NULL;
+    unsigned int cdev, j;
+
+    /* setup watch */
+    snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops);
+    snprintf(path, sizeof(path), "%s/%s/%d", root, type, dom);
+    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);
+    }
+    free(dev);
+    return 0;
+}
+
+static void xenstore_update_be(char *watch, char *type, int dom,
+			       struct devops *ops)
+{
+    struct xendev *xendev;
+    char path[BUFSIZE];
+    unsigned int len, dev;
+
+    len = snprintf(path, sizeof(path), "%s/%s/%d", root, type, dom);
+    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 xendev *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:
+    free(vec);
+}
+
+static void xen_be_evtchn_event(void *opaque)
+{
+    struct xendev *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(char *type, struct devops *ops)
+{
+    return xenstore_scan(type, xen_domid, ops);
+}
+
+int xen_be_bind_evtchn(struct xendev *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) {
+	xendev->local_port = 0;
+	return -1;
+    }
+    qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev),
+			xen_be_evtchn_event, NULL, xendev);
+#if 0
+    xc_evtchn_unmask(xendev->evtchndev, xendev->local_port);
+#endif
+    return 0;
+}
+
+void xen_be_unbind_evtchn(struct xendev *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);
+    xendev->local_port = -1;
+}
+
+int xen_be_send_notify(struct xendev *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 xendev *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..db36ae7
--- /dev/null
+++ b/hw/xen-backend.h
@@ -0,0 +1,116 @@
+#include <stddef.h>
+#include <xs.h>
+#include <xenctrl.h>
+#include <xen/elfnote.h>
+#include <xen/elfstructs.h>
+
+#include <xen/io/xenbus.h>
+
+#include "list.h"
+
+#include "hw.h"
+#include "xen.h"
+
+/*
+ * 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__ < 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
+
+/* ------------------------------------------------------------- */
+
+#define container_of(ptr, type, member) ({                      \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/* ------------------------------------------------------------- */
+
+#define BUFSIZE 1024
+
+struct xendev;
+
+/* 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 devops {
+    size_t    size;
+    uint32_t  flags;
+    void      (*alloc)(struct xendev *xendev);
+    int       (*init)(struct xendev *xendev);
+    int       (*connect)(struct xendev *xendev);
+    void      (*event)(struct xendev *xendev);
+    void      (*disconnect)(struct xendev *xendev);
+    int       (*free)(struct xendev *xendev);
+    void      (*backend_changed)(struct xendev *xendev, const char *node);
+    void      (*frontend_changed)(struct xendev *xendev, const char *node);
+};
+
+struct xendev {
+    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[BUFSIZE];
+    char               *fe;
+    char               *protocol;
+    int                remote_port;
+    int                local_port;
+
+    int                evtchndev;
+    int                gnttabdev;
+
+    struct devops      *ops;
+    struct list_head   next;
+};
+
+/* ------------------------------------------------------------- */
+
+/* variables */
+extern int xen_domid; /* set by cmd line option, in vl.c */
+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 xendev *xendev, const char *node, const char *val);
+int xenstore_write_be_int(struct xendev *xendev, const char *node, int ival);
+char *xenstore_read_be_str(struct xendev *xendev, const char *node);
+int xenstore_read_be_int(struct xendev *xendev, const char *node, int *ival);
+char *xenstore_read_fe_str(struct xendev *xendev, const char *node);
+int xenstore_read_fe_int(struct xendev *xendev, const char *node, int *ival);
+
+const char *xenbus_strstate(enum xenbus_state state);
+struct xendev *xen_be_find_xendev(char *type, int dom, int dev);
+void xen_be_check_state(struct xendev *xendev);
+
+/* xen backend driver bits */
+int xen_be_init(void);
+int xen_be_register(char *type, struct devops *ops);
+int xen_be_set_state(struct xendev *xendev, enum xenbus_state state);
+int xen_be_bind_evtchn(struct xendev *xendev);
+void xen_be_unbind_evtchn(struct xendev *xendev);
+int xen_be_send_notify(struct xendev *xendev);
+void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...)
+    __attribute__ ((format(printf, 3, 4)));
+
diff --git a/hw/xen-machine.c b/hw/xen-machine.c
index 88f0f6e..798c0a7 100644
--- a/hw/xen-machine.c
+++ b/hw/xen-machine.c
@@ -25,7 +25,7 @@
 #include "hw.h"
 #include "boards.h"
 
-#include "xen.h"
+#include "xen-backend.h"
 
 /* -------------------------------------------------------------------- */
 /* variables                                                            */
@@ -54,6 +54,9 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
     }
     env = cpu_init(cpu_model);
     env->halted = 1;
+
+    /* setup xen backend handlers */
+    xen_be_init();
 }
 
 QEMUMachine xenpv_machine = {
-- 
1.5.4.1

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Qemu-devel] [PATCH 3/7] xen: add console backend driver.
  2008-07-28 13:17 ` Gerd Hoffmann
@ 2008-07-28 13:17   ` Gerd Hoffmann
  -1 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

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 |  271 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/xen-machine.c |    2 +
 4 files changed, 277 insertions(+), 0 deletions(-)
 create mode 100644 hw/xen-console.c

diff --git a/Makefile.target b/Makefile.target
index 0451048..05619fa 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -517,6 +517,7 @@ endif
 
 # xen backend driver support
 XEN_OBJS := xen-machine.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 db36ae7..55ffd31 100644
--- a/hw/xen-backend.h
+++ b/hw/xen-backend.h
@@ -114,3 +114,6 @@ int xen_be_send_notify(struct xendev *xendev);
 void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...)
     __attribute__ ((format(printf, 3, 4)));
 
+/* actual backend drivers */
+struct devops xen_console_ops;      /* xen_console.c     */
+
diff --git a/hw/xen-console.c b/hw/xen-console.c
new file mode 100644
index 0000000..9c67f1b
--- /dev/null
+++ b/hw/xen-console.c
@@ -0,0 +1,271 @@
+/*
+ *  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 <malloc.h>
+#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 xen_console {
+    struct xendev     xendev;  /* must be first */
+    struct buffer     buffer;
+    char              console[BUFSIZE];
+    int               ring_ref;
+    void              *sring;
+    CharDriverState   *chr;
+    int               backlog;
+};
+
+static void buffer_append(struct xen_console *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 = 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 = 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 xen_console *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 xen_console *con = opaque;
+    return ring_free_bytes(con);
+}
+
+static void xencons_receive(void *opaque, const uint8_t *buf, int len)
+{
+    struct xen_console *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 xen_console *con)
+{
+    ssize_t len, size;
+
+    size = con->buffer.size - con->buffer.consumed;
+    len = qemu_chr_write(con->chr, con->buffer.data + con->buffer.consumed,
+			 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 xendev *xendev)
+{
+    struct xen_console *con = container_of(xendev, struct xen_console, xendev);
+    char *type;
+
+    if (!serial_hds[con->xendev.dev]) {
+	xen_be_printf(xendev, 1, "serial line %d not configured\n", con->xendev.dev);
+	return -1;
+    }
+
+    /* setup */
+    snprintf(con->console, sizeof(con->console),
+	     "/local/domain/%d/console", con->xendev.dom);
+    con->chr = serial_hds[con->xendev.dev];
+
+    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;
+    }
+
+    return 0;
+}
+
+static int con_connect(struct xendev *xendev)
+{
+    struct xen_console *con = container_of(xendev, struct xen_console, 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);
+    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 xendev *xendev)
+{
+    struct xen_console *con = container_of(xendev, struct xen_console, xendev);
+
+    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 xendev *xendev)
+{
+    struct xen_console *con = container_of(xendev, struct xen_console, xendev);
+
+    buffer_append(con);
+    if (con->buffer.size - con->buffer.consumed)
+	xencons_send(con);
+}
+
+/* -------------------------------------------------------------------- */
+
+struct devops xen_console_ops = {
+    .size       = sizeof(struct xen_console),
+    .flags      = DEVOPS_FLAG_IGNORE_STATE,
+    .init       = con_init,
+    .connect    = con_connect,
+    .event      = con_event,
+    .disconnect = con_disconnect,
+};
diff --git a/hw/xen-machine.c b/hw/xen-machine.c
index 798c0a7..da10982 100644
--- a/hw/xen-machine.c
+++ b/hw/xen-machine.c
@@ -57,6 +57,8 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
 
     /* setup xen backend handlers */
     xen_be_init();
+
+    xen_be_register("console", &xen_console_ops);
 }
 
 QEMUMachine xenpv_machine = {
-- 
1.5.4.1

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 3/7] xen: add console backend driver.
@ 2008-07-28 13:17   ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

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 |  271 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/xen-machine.c |    2 +
 4 files changed, 277 insertions(+), 0 deletions(-)
 create mode 100644 hw/xen-console.c

diff --git a/Makefile.target b/Makefile.target
index 0451048..05619fa 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -517,6 +517,7 @@ endif
 
 # xen backend driver support
 XEN_OBJS := xen-machine.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 db36ae7..55ffd31 100644
--- a/hw/xen-backend.h
+++ b/hw/xen-backend.h
@@ -114,3 +114,6 @@ int xen_be_send_notify(struct xendev *xendev);
 void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...)
     __attribute__ ((format(printf, 3, 4)));
 
+/* actual backend drivers */
+struct devops xen_console_ops;      /* xen_console.c     */
+
diff --git a/hw/xen-console.c b/hw/xen-console.c
new file mode 100644
index 0000000..9c67f1b
--- /dev/null
+++ b/hw/xen-console.c
@@ -0,0 +1,271 @@
+/*
+ *  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 <malloc.h>
+#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 xen_console {
+    struct xendev     xendev;  /* must be first */
+    struct buffer     buffer;
+    char              console[BUFSIZE];
+    int               ring_ref;
+    void              *sring;
+    CharDriverState   *chr;
+    int               backlog;
+};
+
+static void buffer_append(struct xen_console *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 = 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 = 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 xen_console *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 xen_console *con = opaque;
+    return ring_free_bytes(con);
+}
+
+static void xencons_receive(void *opaque, const uint8_t *buf, int len)
+{
+    struct xen_console *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 xen_console *con)
+{
+    ssize_t len, size;
+
+    size = con->buffer.size - con->buffer.consumed;
+    len = qemu_chr_write(con->chr, con->buffer.data + con->buffer.consumed,
+			 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 xendev *xendev)
+{
+    struct xen_console *con = container_of(xendev, struct xen_console, xendev);
+    char *type;
+
+    if (!serial_hds[con->xendev.dev]) {
+	xen_be_printf(xendev, 1, "serial line %d not configured\n", con->xendev.dev);
+	return -1;
+    }
+
+    /* setup */
+    snprintf(con->console, sizeof(con->console),
+	     "/local/domain/%d/console", con->xendev.dom);
+    con->chr = serial_hds[con->xendev.dev];
+
+    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;
+    }
+
+    return 0;
+}
+
+static int con_connect(struct xendev *xendev)
+{
+    struct xen_console *con = container_of(xendev, struct xen_console, 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);
+    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 xendev *xendev)
+{
+    struct xen_console *con = container_of(xendev, struct xen_console, xendev);
+
+    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 xendev *xendev)
+{
+    struct xen_console *con = container_of(xendev, struct xen_console, xendev);
+
+    buffer_append(con);
+    if (con->buffer.size - con->buffer.consumed)
+	xencons_send(con);
+}
+
+/* -------------------------------------------------------------------- */
+
+struct devops xen_console_ops = {
+    .size       = sizeof(struct xen_console),
+    .flags      = DEVOPS_FLAG_IGNORE_STATE,
+    .init       = con_init,
+    .connect    = con_connect,
+    .event      = con_event,
+    .disconnect = con_disconnect,
+};
diff --git a/hw/xen-machine.c b/hw/xen-machine.c
index 798c0a7..da10982 100644
--- a/hw/xen-machine.c
+++ b/hw/xen-machine.c
@@ -57,6 +57,8 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
 
     /* setup xen backend handlers */
     xen_be_init();
+
+    xen_be_register("console", &xen_console_ops);
 }
 
 QEMUMachine xenpv_machine = {
-- 
1.5.4.1

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
  2008-07-28 13:17 ` Gerd Hoffmann
@ 2008-07-28 13:17   ` Gerd Hoffmann
  -1 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

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 unstable (aka son-to-be 3.3).

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 Makefile.target      |    2 +-
 hw/xen-backend.h     |    4 +
 hw/xen-framebuffer.c |  925 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/xen-machine.c     |    5 +
 4 files changed, 935 insertions(+), 1 deletions(-)
 create mode 100644 hw/xen-framebuffer.c

diff --git a/Makefile.target b/Makefile.target
index 05619fa..66d41ee 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -517,7 +517,7 @@ endif
 
 # xen backend driver support
 XEN_OBJS := xen-machine.o xen-backend.o
-XEN_OBJS += xen-console.o
+XEN_OBJS += xen-console.o xen-framebuffer.o
 ifeq ($(CONFIG_XEN), yes)
   OBJS += $(XEN_OBJS)
   LIBS += $(XEN_LIBS)
diff --git a/hw/xen-backend.h b/hw/xen-backend.h
index 55ffd31..3facf90 100644
--- a/hw/xen-backend.h
+++ b/hw/xen-backend.h
@@ -116,4 +116,8 @@ void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...)
 
 /* actual backend drivers */
 struct devops xen_console_ops;      /* xen_console.c     */
+struct devops xen_kbdmouse_ops;     /* xen_framebuffer.c */
+struct devops xen_framebuffer_ops;  /* xen_framebuffer.c */
+
+void xen_set_display(int domid, DisplayState *ds);
 
diff --git a/hw/xen-framebuffer.c b/hw/xen-framebuffer.c
new file mode 100644
index 0000000..716eafd
--- /dev/null
+++ b/hw/xen-framebuffer.c
@@ -0,0 +1,925 @@
+/*
+ *  xen paravirt framebuffer backend
+ *
+ *  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 xen_common {
+    struct xendev     xendev;  /* must be first */
+    void              *page;
+    DisplayState      *ds;
+};
+
+struct xen_input {
+    struct xen_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 xen_fb {
+    struct xen_common c;
+    size_t            fb_len;
+    int               row_stride;
+    int               depth;
+    int               width;
+    int               height;
+    int               offset;
+    void              *pixels;
+    int               feature_update;
+    int               refresh_period;
+    int               bug_trigger;
+
+    struct {
+	int x,y,w,h;
+    } up_rects[UP_QUEUE];
+    int               up_count;
+    int               up_fullscreen;
+};
+
+/* -------------------------------------------------------------------- */
+
+static int common_bind(struct xen_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;
+
+    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);
+
+    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;
+    return 0;
+}
+
+static void common_unbind(struct xen_common *c)
+{
+    if (c->page) {
+	munmap(c->page, XC_PAGE_SIZE);
+	c->page = NULL;
+    }
+    xen_be_unbind_evtchn(&c->xendev);
+}
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * 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
+
+};
+
+static unsigned char scancode2linux[512];
+
+/* Send an event to the keyboard frontend driver */
+static int xenfb_kbd_event(struct xen_input *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;
+
+    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 xen_input *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 xen_input *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 xen_input *xenfb,
+			       int abs_x, int abs_y, int rel_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.rel_z = rel_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 xen_input *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 xen_input *xenfb = opaque;
+    int i;
+
+    if (xenfb->abs_pointer_wanted)
+	xenfb_send_position(xenfb,
+			    dx * (xenfb->c.ds->width - 1) / 0x7fff,
+			    dy * (xenfb->c.ds->height - 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 xendev *xendev)
+{
+    struct xen_input *in = container_of(xendev, struct xen_input, c.xendev);
+    static int first = 1;
+    int i;
+
+    if (first) {
+	/* Prepare scancode mapping table */
+	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];
+	}
+	first = 0;
+    }
+
+    if (!in->c.ds) {
+        /* xen_set_display() below will set that and trigger us again */
+        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 xendev *xendev)
+{
+    struct xen_input *in = container_of(xendev, struct xen_input, 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 xendev *xendev)
+{
+    struct xen_input *in = container_of(xendev, struct xen_input, 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 xendev *xendev)
+{
+    struct xen_input *xenfb = container_of(xendev, struct xen_input, 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 xen_fb *xenfb)
+{
+    struct xenfb_page *page = xenfb->c.page;
+    char *protocol = xenfb->c.xendev.protocol;
+    int n_fbmfns;
+    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
+    }
+
+    n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
+    n_fbdirs = n_fbmfns * mode / 8;
+    n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
+
+    pgmfns = malloc(sizeof(unsigned long) * n_fbdirs);
+    fbmfns = malloc(sizeof(unsigned long) * n_fbmfns);
+    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, n_fbmfns, 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, n_fbmfns);
+    if (xenfb->pixels == NULL)
+	goto out;
+
+    ret = 0; /* all is fine */
+
+out:
+    if (pgmfns)
+	free(pgmfns);
+    if (fbmfns)
+	free(fbmfns);
+    return ret;
+}
+
+static int xenfb_configure_fb(struct xen_fb *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;
+    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 *)(xenfb->c.ds->data				\
+			       + (line * xenfb->c.ds->linesize)		\
+			       + (x * xenfb->c.ds->depth / 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 + xenfb->c.ds->depth / 8); \
+	}								\
+    }
+
+
+/* This copies data from the guest framebuffer region, into QEMU's copy
+ * NB. QEMU's copy is stored in the pixel format of a) the local X
+ * server (SDL case) or b) the current VNC client pixel format.
+ * When shifting between colour depths we preserve the MSB.
+ */
+static void xenfb_guest_copy(struct xen_fb *xenfb, int x, int y, int w, int h)
+{
+    int line;
+
+    if (1 /* !xenfb->c.ds->shared_buf */) {
+	if (xenfb->depth == xenfb->c.ds->depth) { /* Perfect match can use fast path */
+	    for (line = y ; line < (y+h) ; line++) {
+		memcpy(xenfb->c.ds->data + (line * xenfb->c.ds->linesize) + (x * xenfb->c.ds->depth / 8),
+		       xenfb->pixels + xenfb->offset + (line * xenfb->row_stride) + (x * xenfb->depth / 8),
+		       w * xenfb->depth / 8);
+	    }
+	} else { /* Mismatch requires slow pixel munging */
+	    /* 8 bit == r:3 g:3 b:2 */
+	    /* 16 bit == r:5 g:6 b:5 */
+	    /* 24 bit == r:8 g:8 b:8 */
+	    /* 32 bit == r:8 g:8 b:8 (padding:8) */
+	    if (xenfb->depth == 8) {
+		if (xenfb->c.ds->depth == 16) {
+		    BLT(uint8_t, uint16_t,   3, 3, 2,   5, 6, 5);
+		} else if (xenfb->c.ds->depth == 32) {
+		    BLT(uint8_t, uint32_t,   3, 3, 2,   8, 8, 8);
+		}
+	    } else if (xenfb->depth == 16) {
+		if (xenfb->c.ds->depth == 8) {
+		    BLT(uint16_t, uint8_t,   5, 6, 5,   3, 3, 2);
+		} else if (xenfb->c.ds->depth == 32) {
+		    BLT(uint16_t, uint32_t,  5, 6, 5,   8, 8, 8);
+		}
+	    } else if (xenfb->depth == 24 || xenfb->depth == 32) {
+		if (xenfb->c.ds->depth == 8) {
+		    BLT(uint32_t, uint8_t,   8, 8, 8,   3, 3, 2);
+		} else if (xenfb->c.ds->depth == 16) {
+		    BLT(uint32_t, uint16_t,  8, 8, 8,   5, 6, 5);
+		} else if (xenfb->c.ds->depth == 32) {
+		    BLT(uint32_t, uint32_t,  8, 8, 8,   8, 8, 8);
+		}
+	    }
+	}
+    }
+    dpy_update(xenfb->c.ds, x, y, w, h);
+}
+
+#ifdef XENFB_TYPE_REFRESH_PERIOD
+static int xenfb_queue_full(struct xen_fb *xenfb)
+{
+    struct xenfb_page *page = xenfb->c.page;
+    uint32_t cons, prod;
+
+    prod = page->in_prod;
+    cons = page->in_cons;
+    return prod - cons == XENFB_IN_RING_LEN;
+}
+
+static void xenfb_send_event(struct xen_fb *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 xen_fb *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 xen_fb *xenfb = opaque;
+    int i;
+
+    if (xenfb->feature_update) {
+#ifdef XENFB_TYPE_REFRESH_PERIOD
+	int period;
+
+	if (xenfb_queue_full(xenfb))
+	    return;
+
+        /*
+         * TODO: xen's qemu-dm seems to have some patches to
+         *       make the qemu display code avoid unneeded
+         *       work.
+         *        - Port them over.
+         *        - Put ds->idle back into use then.
+         *        - Same goes for ds->shared_buf btw.
+         */
+	if (0 /* xenfb->c.ds->idle */)
+	    period =  XENFB_NO_REFRESH;
+	else {
+	    period = xenfb->c.ds->gui_timer_interval;
+	    if (!period)
+		period = 30; // GUI_REFRESH_INTERVAL (see vl.c)
+	}
+
+	if (xenfb->refresh_period != period) {
+	    dprintf("%s: %d\n", __FUNCTION__, period);
+	    xenfb_send_refresh_period(xenfb, period);
+	    xenfb->refresh_period = 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->width != xenfb->c.ds->width || xenfb->height != xenfb->c.ds->height) {
+	xen_be_printf(&xenfb->c.xendev, 1, "update: resizing\n");
+	dpy_resize(xenfb->c.ds, xenfb->width, xenfb->height);
+	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;
+}
+
+static void xenfb_handle_events(struct xen_fb *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;
+}
+
+/* QEMU display state changed, so refresh the framebuffer copy */
+static void xenfb_invalidate(void *opaque)
+{
+    struct xen_fb *xenfb = opaque;
+    xenfb->up_fullscreen = 1;
+}
+
+static int fb_init(struct xendev *xendev)
+{
+    struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev);
+
+    if (!fb->c.ds) {
+        /* xen_set_display() below will set that and trigger us again */
+	xen_be_printf(xendev, 1, "ds not set (yet)\n");
+	return -1;
+    }
+
+    fb->refresh_period = -1;
+
+#ifdef XENFB_TYPE_RESIZE
+    xenstore_write_be_int(xendev, "feature-resize", 1);
+#endif
+    return 0;
+}
+
+static int fb_connect(struct xendev *xendev)
+{
+    struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev);
+    struct xenfb_page *fb_page;
+    int videoram;
+    int rc;
+
+    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);
+
+    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;
+
+    graphic_console_init(fb->c.ds,
+			 xenfb_update,
+			 xenfb_invalidate,
+			 NULL,
+			 NULL,
+			 fb);
+
+    xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n",
+		  fb->feature_update, videoram);
+
+    return 0;
+}
+
+static void fb_disconnect(struct xendev *xendev)
+{
+    struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev);
+
+    /* FIXME: Hmm, un-init gfx display? can qemu handle that? */
+    common_unbind(&fb->c);
+}
+
+static void fb_frontend_changed(struct xendev *xendev, const char *node)
+{
+    struct xen_fb *fb = container_of(xendev, struct xen_fb, 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_set_state(xendev, XenbusStateConnected);
+	fb->bug_trigger = 1; /* only once */
+    }
+}
+
+static void fb_event(struct xendev *xendev)
+{
+    struct xen_fb *xenfb = container_of(xendev, struct xen_fb, c.xendev);
+
+    xenfb_handle_events(xenfb);
+    xen_be_send_notify(&xenfb->c.xendev);
+}
+
+/* -------------------------------------------------------------------- */
+
+struct devops xen_kbdmouse_ops = {
+    .size       = sizeof(struct xen_input),
+    .init       = input_init,
+    .connect    = input_connect,
+    .disconnect = input_disconnect,
+    .event      = input_event,
+};
+
+struct devops xen_framebuffer_ops = {
+    .size       = sizeof(struct xen_fb),
+    .init       = fb_init,
+    .connect    = fb_connect,
+    .disconnect = fb_disconnect,
+    .event      = fb_event,
+    .frontend_changed = fb_frontend_changed,
+};
+
+static void xen_set_display_type(int domid, char *type, DisplayState *ds)
+{
+    struct xendev *xendev;
+    struct xen_common *c;
+
+    xendev = xen_be_find_xendev(type, domid, 0);
+    if (!xendev)
+	return;
+    c = container_of(xendev, struct xen_common, xendev);
+    c->ds = ds;
+    xen_be_printf(xendev, 1, "ds is %p\n", ds);
+    /* retry ->init() */
+    xen_be_check_state(xendev);
+}
+
+void xen_set_display(int domid, DisplayState *ds)
+{
+    xen_set_display_type(domid, "vkbd", ds);
+    xen_set_display_type(domid, "vfb", ds);
+}
diff --git a/hw/xen-machine.c b/hw/xen-machine.c
index da10982..c03cb53 100644
--- a/hw/xen-machine.c
+++ b/hw/xen-machine.c
@@ -59,6 +59,11 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
     xen_be_init();
 
     xen_be_register("console", &xen_console_ops);
+    xen_be_register("vkbd", &xen_kbdmouse_ops);
+    xen_be_register("vfb", &xen_framebuffer_ops);
+
+    /* setup framebuffer */
+    xen_set_display(xen_domid, ds);
 }
 
 QEMUMachine xenpv_machine = {
-- 
1.5.4.1

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 4/7] xen: add framebuffer backend driver
@ 2008-07-28 13:17   ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

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 unstable (aka son-to-be 3.3).

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 Makefile.target      |    2 +-
 hw/xen-backend.h     |    4 +
 hw/xen-framebuffer.c |  925 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/xen-machine.c     |    5 +
 4 files changed, 935 insertions(+), 1 deletions(-)
 create mode 100644 hw/xen-framebuffer.c

diff --git a/Makefile.target b/Makefile.target
index 05619fa..66d41ee 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -517,7 +517,7 @@ endif
 
 # xen backend driver support
 XEN_OBJS := xen-machine.o xen-backend.o
-XEN_OBJS += xen-console.o
+XEN_OBJS += xen-console.o xen-framebuffer.o
 ifeq ($(CONFIG_XEN), yes)
   OBJS += $(XEN_OBJS)
   LIBS += $(XEN_LIBS)
diff --git a/hw/xen-backend.h b/hw/xen-backend.h
index 55ffd31..3facf90 100644
--- a/hw/xen-backend.h
+++ b/hw/xen-backend.h
@@ -116,4 +116,8 @@ void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...)
 
 /* actual backend drivers */
 struct devops xen_console_ops;      /* xen_console.c     */
+struct devops xen_kbdmouse_ops;     /* xen_framebuffer.c */
+struct devops xen_framebuffer_ops;  /* xen_framebuffer.c */
+
+void xen_set_display(int domid, DisplayState *ds);
 
diff --git a/hw/xen-framebuffer.c b/hw/xen-framebuffer.c
new file mode 100644
index 0000000..716eafd
--- /dev/null
+++ b/hw/xen-framebuffer.c
@@ -0,0 +1,925 @@
+/*
+ *  xen paravirt framebuffer backend
+ *
+ *  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 xen_common {
+    struct xendev     xendev;  /* must be first */
+    void              *page;
+    DisplayState      *ds;
+};
+
+struct xen_input {
+    struct xen_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 xen_fb {
+    struct xen_common c;
+    size_t            fb_len;
+    int               row_stride;
+    int               depth;
+    int               width;
+    int               height;
+    int               offset;
+    void              *pixels;
+    int               feature_update;
+    int               refresh_period;
+    int               bug_trigger;
+
+    struct {
+	int x,y,w,h;
+    } up_rects[UP_QUEUE];
+    int               up_count;
+    int               up_fullscreen;
+};
+
+/* -------------------------------------------------------------------- */
+
+static int common_bind(struct xen_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;
+
+    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);
+
+    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;
+    return 0;
+}
+
+static void common_unbind(struct xen_common *c)
+{
+    if (c->page) {
+	munmap(c->page, XC_PAGE_SIZE);
+	c->page = NULL;
+    }
+    xen_be_unbind_evtchn(&c->xendev);
+}
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * 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
+
+};
+
+static unsigned char scancode2linux[512];
+
+/* Send an event to the keyboard frontend driver */
+static int xenfb_kbd_event(struct xen_input *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;
+
+    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 xen_input *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 xen_input *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 xen_input *xenfb,
+			       int abs_x, int abs_y, int rel_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.rel_z = rel_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 xen_input *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 xen_input *xenfb = opaque;
+    int i;
+
+    if (xenfb->abs_pointer_wanted)
+	xenfb_send_position(xenfb,
+			    dx * (xenfb->c.ds->width - 1) / 0x7fff,
+			    dy * (xenfb->c.ds->height - 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 xendev *xendev)
+{
+    struct xen_input *in = container_of(xendev, struct xen_input, c.xendev);
+    static int first = 1;
+    int i;
+
+    if (first) {
+	/* Prepare scancode mapping table */
+	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];
+	}
+	first = 0;
+    }
+
+    if (!in->c.ds) {
+        /* xen_set_display() below will set that and trigger us again */
+        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 xendev *xendev)
+{
+    struct xen_input *in = container_of(xendev, struct xen_input, 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 xendev *xendev)
+{
+    struct xen_input *in = container_of(xendev, struct xen_input, 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 xendev *xendev)
+{
+    struct xen_input *xenfb = container_of(xendev, struct xen_input, 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 xen_fb *xenfb)
+{
+    struct xenfb_page *page = xenfb->c.page;
+    char *protocol = xenfb->c.xendev.protocol;
+    int n_fbmfns;
+    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
+    }
+
+    n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
+    n_fbdirs = n_fbmfns * mode / 8;
+    n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
+
+    pgmfns = malloc(sizeof(unsigned long) * n_fbdirs);
+    fbmfns = malloc(sizeof(unsigned long) * n_fbmfns);
+    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, n_fbmfns, 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, n_fbmfns);
+    if (xenfb->pixels == NULL)
+	goto out;
+
+    ret = 0; /* all is fine */
+
+out:
+    if (pgmfns)
+	free(pgmfns);
+    if (fbmfns)
+	free(fbmfns);
+    return ret;
+}
+
+static int xenfb_configure_fb(struct xen_fb *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;
+    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 *)(xenfb->c.ds->data				\
+			       + (line * xenfb->c.ds->linesize)		\
+			       + (x * xenfb->c.ds->depth / 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 + xenfb->c.ds->depth / 8); \
+	}								\
+    }
+
+
+/* This copies data from the guest framebuffer region, into QEMU's copy
+ * NB. QEMU's copy is stored in the pixel format of a) the local X
+ * server (SDL case) or b) the current VNC client pixel format.
+ * When shifting between colour depths we preserve the MSB.
+ */
+static void xenfb_guest_copy(struct xen_fb *xenfb, int x, int y, int w, int h)
+{
+    int line;
+
+    if (1 /* !xenfb->c.ds->shared_buf */) {
+	if (xenfb->depth == xenfb->c.ds->depth) { /* Perfect match can use fast path */
+	    for (line = y ; line < (y+h) ; line++) {
+		memcpy(xenfb->c.ds->data + (line * xenfb->c.ds->linesize) + (x * xenfb->c.ds->depth / 8),
+		       xenfb->pixels + xenfb->offset + (line * xenfb->row_stride) + (x * xenfb->depth / 8),
+		       w * xenfb->depth / 8);
+	    }
+	} else { /* Mismatch requires slow pixel munging */
+	    /* 8 bit == r:3 g:3 b:2 */
+	    /* 16 bit == r:5 g:6 b:5 */
+	    /* 24 bit == r:8 g:8 b:8 */
+	    /* 32 bit == r:8 g:8 b:8 (padding:8) */
+	    if (xenfb->depth == 8) {
+		if (xenfb->c.ds->depth == 16) {
+		    BLT(uint8_t, uint16_t,   3, 3, 2,   5, 6, 5);
+		} else if (xenfb->c.ds->depth == 32) {
+		    BLT(uint8_t, uint32_t,   3, 3, 2,   8, 8, 8);
+		}
+	    } else if (xenfb->depth == 16) {
+		if (xenfb->c.ds->depth == 8) {
+		    BLT(uint16_t, uint8_t,   5, 6, 5,   3, 3, 2);
+		} else if (xenfb->c.ds->depth == 32) {
+		    BLT(uint16_t, uint32_t,  5, 6, 5,   8, 8, 8);
+		}
+	    } else if (xenfb->depth == 24 || xenfb->depth == 32) {
+		if (xenfb->c.ds->depth == 8) {
+		    BLT(uint32_t, uint8_t,   8, 8, 8,   3, 3, 2);
+		} else if (xenfb->c.ds->depth == 16) {
+		    BLT(uint32_t, uint16_t,  8, 8, 8,   5, 6, 5);
+		} else if (xenfb->c.ds->depth == 32) {
+		    BLT(uint32_t, uint32_t,  8, 8, 8,   8, 8, 8);
+		}
+	    }
+	}
+    }
+    dpy_update(xenfb->c.ds, x, y, w, h);
+}
+
+#ifdef XENFB_TYPE_REFRESH_PERIOD
+static int xenfb_queue_full(struct xen_fb *xenfb)
+{
+    struct xenfb_page *page = xenfb->c.page;
+    uint32_t cons, prod;
+
+    prod = page->in_prod;
+    cons = page->in_cons;
+    return prod - cons == XENFB_IN_RING_LEN;
+}
+
+static void xenfb_send_event(struct xen_fb *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 xen_fb *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 xen_fb *xenfb = opaque;
+    int i;
+
+    if (xenfb->feature_update) {
+#ifdef XENFB_TYPE_REFRESH_PERIOD
+	int period;
+
+	if (xenfb_queue_full(xenfb))
+	    return;
+
+        /*
+         * TODO: xen's qemu-dm seems to have some patches to
+         *       make the qemu display code avoid unneeded
+         *       work.
+         *        - Port them over.
+         *        - Put ds->idle back into use then.
+         *        - Same goes for ds->shared_buf btw.
+         */
+	if (0 /* xenfb->c.ds->idle */)
+	    period =  XENFB_NO_REFRESH;
+	else {
+	    period = xenfb->c.ds->gui_timer_interval;
+	    if (!period)
+		period = 30; // GUI_REFRESH_INTERVAL (see vl.c)
+	}
+
+	if (xenfb->refresh_period != period) {
+	    dprintf("%s: %d\n", __FUNCTION__, period);
+	    xenfb_send_refresh_period(xenfb, period);
+	    xenfb->refresh_period = 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->width != xenfb->c.ds->width || xenfb->height != xenfb->c.ds->height) {
+	xen_be_printf(&xenfb->c.xendev, 1, "update: resizing\n");
+	dpy_resize(xenfb->c.ds, xenfb->width, xenfb->height);
+	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;
+}
+
+static void xenfb_handle_events(struct xen_fb *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;
+}
+
+/* QEMU display state changed, so refresh the framebuffer copy */
+static void xenfb_invalidate(void *opaque)
+{
+    struct xen_fb *xenfb = opaque;
+    xenfb->up_fullscreen = 1;
+}
+
+static int fb_init(struct xendev *xendev)
+{
+    struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev);
+
+    if (!fb->c.ds) {
+        /* xen_set_display() below will set that and trigger us again */
+	xen_be_printf(xendev, 1, "ds not set (yet)\n");
+	return -1;
+    }
+
+    fb->refresh_period = -1;
+
+#ifdef XENFB_TYPE_RESIZE
+    xenstore_write_be_int(xendev, "feature-resize", 1);
+#endif
+    return 0;
+}
+
+static int fb_connect(struct xendev *xendev)
+{
+    struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev);
+    struct xenfb_page *fb_page;
+    int videoram;
+    int rc;
+
+    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);
+
+    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;
+
+    graphic_console_init(fb->c.ds,
+			 xenfb_update,
+			 xenfb_invalidate,
+			 NULL,
+			 NULL,
+			 fb);
+
+    xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n",
+		  fb->feature_update, videoram);
+
+    return 0;
+}
+
+static void fb_disconnect(struct xendev *xendev)
+{
+    struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev);
+
+    /* FIXME: Hmm, un-init gfx display? can qemu handle that? */
+    common_unbind(&fb->c);
+}
+
+static void fb_frontend_changed(struct xendev *xendev, const char *node)
+{
+    struct xen_fb *fb = container_of(xendev, struct xen_fb, 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_set_state(xendev, XenbusStateConnected);
+	fb->bug_trigger = 1; /* only once */
+    }
+}
+
+static void fb_event(struct xendev *xendev)
+{
+    struct xen_fb *xenfb = container_of(xendev, struct xen_fb, c.xendev);
+
+    xenfb_handle_events(xenfb);
+    xen_be_send_notify(&xenfb->c.xendev);
+}
+
+/* -------------------------------------------------------------------- */
+
+struct devops xen_kbdmouse_ops = {
+    .size       = sizeof(struct xen_input),
+    .init       = input_init,
+    .connect    = input_connect,
+    .disconnect = input_disconnect,
+    .event      = input_event,
+};
+
+struct devops xen_framebuffer_ops = {
+    .size       = sizeof(struct xen_fb),
+    .init       = fb_init,
+    .connect    = fb_connect,
+    .disconnect = fb_disconnect,
+    .event      = fb_event,
+    .frontend_changed = fb_frontend_changed,
+};
+
+static void xen_set_display_type(int domid, char *type, DisplayState *ds)
+{
+    struct xendev *xendev;
+    struct xen_common *c;
+
+    xendev = xen_be_find_xendev(type, domid, 0);
+    if (!xendev)
+	return;
+    c = container_of(xendev, struct xen_common, xendev);
+    c->ds = ds;
+    xen_be_printf(xendev, 1, "ds is %p\n", ds);
+    /* retry ->init() */
+    xen_be_check_state(xendev);
+}
+
+void xen_set_display(int domid, DisplayState *ds)
+{
+    xen_set_display_type(domid, "vkbd", ds);
+    xen_set_display_type(domid, "vfb", ds);
+}
diff --git a/hw/xen-machine.c b/hw/xen-machine.c
index da10982..c03cb53 100644
--- a/hw/xen-machine.c
+++ b/hw/xen-machine.c
@@ -59,6 +59,11 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
     xen_be_init();
 
     xen_be_register("console", &xen_console_ops);
+    xen_be_register("vkbd", &xen_kbdmouse_ops);
+    xen_be_register("vfb", &xen_framebuffer_ops);
+
+    /* setup framebuffer */
+    xen_set_display(xen_domid, ds);
 }
 
 QEMUMachine xenpv_machine = {
-- 
1.5.4.1

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Qemu-devel] [PATCH 5/7] xen: add block device backend driver.
  2008-07-28 13:17 ` Gerd Hoffmann
@ 2008-07-28 13:17   ` Gerd Hoffmann
  -1 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

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    |  681 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/xen-machine.c |    1 +
 sysemu.h         |    2 +-
 vl.c             |    4 +
 7 files changed, 793 insertions(+), 2 deletions(-)
 create mode 100644 hw/xen-blkif.h
 create mode 100644 hw/xen-disk.c

diff --git a/Makefile.target b/Makefile.target
index 66d41ee..2d599d2 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -517,7 +517,7 @@ endif
 
 # xen backend driver support
 XEN_OBJS := xen-machine.o xen-backend.o
-XEN_OBJS += xen-console.o xen-framebuffer.o
+XEN_OBJS += xen-console.o xen-framebuffer.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 3facf90..941b0a6 100644
--- a/hw/xen-backend.h
+++ b/hw/xen-backend.h
@@ -10,6 +10,7 @@
 
 #include "hw.h"
 #include "xen.h"
+#include "sysemu.h"
 
 /*
  * tweaks needed to build with different xen versions
@@ -118,6 +119,7 @@ void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...)
 struct devops xen_console_ops;      /* xen_console.c     */
 struct devops xen_kbdmouse_ops;     /* xen_framebuffer.c */
 struct devops xen_framebuffer_ops;  /* xen_framebuffer.c */
+struct devops xen_blkdev_ops;       /* xen_disk.c        */
 
 void xen_set_display(int domid, DisplayState *ds);
 
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..6947363
--- /dev/null
+++ b/hw/xen-disk.c
@@ -0,0 +1,681 @@
+/*
+ *  xen paravirt block device backend
+ *
+ *  FIXME: the code is designed to handle multiple outstanding
+ *  requests (using aio or using threads), which isn't used right
+ *  now due to limitations of the qemu block driver interface.
+ *
+ *
+ *  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 <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/utsname.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 "list.h"
+
+#include "hw.h"
+#include "block_int.h"
+#include "qemu-char.h"
+#include "xen-blkif.h"
+#include "xen-backend.h"
+
+/* ------------------------------------------------------------- */
+
+#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;
+
+    struct blkdev       *blkdev;
+    struct list_head    list;
+};
+
+struct blkdev {
+    struct xendev       xendev;  /* must be first */
+    char                *params;
+    char                *mode;
+    char                *type;
+    char                *dev;
+    char                *fileproto;
+    char                *filename;
+    int                 ring_ref;
+    void                *sring;
+    int                 file;
+    int64_t             file_blk;
+    int64_t             file_size;
+    int                 protocol;
+    blkif_back_rings_t  rings;
+    int                 more_work;
+    int                 cnt_map;
+
+    /* request lists */
+    struct list_head    inflight;
+    struct list_head    finished;
+    struct list_head    freelist;
+    int                 requests;
+
+    /* qemu block driver */
+    BlockDriverState    *bs;
+};
+
+static int syncwrite    = 0;
+static int batch_maps   = 0;
+static int max_requests = 32;
+
+/* ------------------------------------------------------------- */
+
+static struct ioreq *ioreq_start(struct blkdev *blkdev)
+{
+    struct ioreq *ioreq = NULL;
+
+    if (list_empty(&blkdev->freelist)) {
+	if (blkdev->requests >= max_requests)
+	    goto out;
+	/* allocate new struct */
+	ioreq = malloc(sizeof(*ioreq));
+	if (NULL == ioreq)
+	    goto out;
+	memset(ioreq, 0, sizeof(*ioreq));
+	ioreq->blkdev = blkdev;
+	blkdev->requests++;
+    } else {
+	/* get one from freelist */
+	ioreq = list_entry(blkdev->freelist.next, struct ioreq, list);
+	list_del(&ioreq->list);
+    }
+    list_add_tail(&ioreq->list, &blkdev->inflight);
+
+out:
+    return ioreq;
+}
+
+static void ioreq_finish(struct ioreq *ioreq)
+{
+    struct blkdev *blkdev = ioreq->blkdev;
+
+    list_del(&ioreq->list);
+    list_add_tail(&ioreq->list, &blkdev->finished);
+}
+
+static void ioreq_release(struct ioreq *ioreq)
+{
+    struct blkdev *blkdev = ioreq->blkdev;
+
+    list_del(&ioreq->list);
+    memset(ioreq, 0, sizeof(*ioreq));
+    ioreq->blkdev = blkdev;
+    list_add_tail(&ioreq->list, &blkdev->freelist);
+}
+
+/*
+ * translate request into iovec + start offset + end offset
+ * do sanity checks along the way
+ */
+static int ioreq_parse(struct ioreq *ioreq)
+{
+    struct blkdev *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 (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 (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(struct ioreq *ioreq)
+{
+    struct blkdev *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);
+    return 0;
+
+err:
+    ioreq->status = BLKIF_RSP_ERROR;
+    return -1;
+}
+
+static int blk_send_response_one(struct ioreq *ioreq)
+{
+    struct blkdev     *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 blkdev *blkdev)
+{
+    struct list_head *item, *safe;
+    struct ioreq *ioreq;
+    int send_notify = 0;
+
+    list_for_each_safe(item, safe, &blkdev->finished) {
+	ioreq = list_entry(item, struct ioreq, list);
+	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 blkdev *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 blkdev *blkdev)
+{
+    RING_IDX rc, rp;
+    struct ioreq *ioreq;
+
+    do {
+	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'. */
+
+	/* Limit #of requests we queue up for I/O so we ack requests
+	 * faster if busy.  Improves backend/frontend parallelism and
+	 * reduces evchn signaling. */
+	if (rp > rc + (max_requests >> 2)) {
+	    rp = rc + (max_requests >> 2);
+	    blkdev->more_work++;
+	}
+
+	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;
+	    }
+
+	    /* run i/o in qemu mode */
+	    ioreq_runio_qemu(ioreq);
+	    ioreq_finish(ioreq);
+	}
+	blk_send_response_all(blkdev);
+
+    } while (blkdev->more_work);
+}
+
+/* ------------------------------------------------------------- */
+
+static void blk_alloc(struct xendev *xendev)
+{
+    struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev);
+
+    INIT_LIST_HEAD(&blkdev->inflight);
+    INIT_LIST_HEAD(&blkdev->finished);
+    INIT_LIST_HEAD(&blkdev->freelist);
+}
+
+static int blk_init(struct xendev *xendev)
+{
+    struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev);
+    int mode, qflags, have_barriers, index, 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");
+
+    /* 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 | VDISK_REMOVABLE | VDISK_CDROM;
+    }
+
+    /* init qemu block driver */
+    index = (blkdev->xendev.dev - 202 * 256) / 16;
+    index = drive_get_index(IF_XEN, 0, index);
+    if (-1 == index) {
+	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 {
+	blkdev->bs = drives_table[index].bdrv;
+    }
+    blkdev->file_blk  = BLOCK_SIZE;
+    blkdev->file_size = bdrv_getlength(blkdev->bs);
+    if (blkdev->file_size < 0)
+	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 xendev *xendev)
+{
+    struct blkdev *blkdev = container_of(xendev, struct blkdev, 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 (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 xendev *xendev)
+{
+    struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev);
+
+    if (-1 != blkdev->file) {
+	close(blkdev->file);
+	blkdev->file = -1;
+    }
+    if (blkdev->bs) {
+	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 xendev *xendev)
+{
+    struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev);
+    struct list_head *item, *safe;
+    struct ioreq *ioreq;
+
+    list_for_each_safe(item, safe, &blkdev->freelist) {
+	ioreq = list_entry(item, struct ioreq, list);
+	list_del(&ioreq->list);
+	free(ioreq);
+    }
+
+    free(blkdev->params);
+    free(blkdev->mode);
+    return 0;
+}
+
+static void blk_event(struct xendev *xendev)
+{
+    struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev);
+    blk_handle_requests(blkdev);
+}
+
+struct devops xen_blkdev_ops = {
+    .size       = sizeof(struct blkdev),
+    .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.c b/hw/xen-machine.c
index c03cb53..1b647a2 100644
--- a/hw/xen-machine.c
+++ b/hw/xen-machine.c
@@ -61,6 +61,7 @@ static void xenpv_init(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_set_display(xen_domid, ds);
diff --git a/sysemu.h b/sysemu.h
index b12fae0..49e75b1 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -113,7 +113,7 @@ extern unsigned int nb_prom_envs;
 #endif
 
 typedef enum {
-    IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD
+    IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_XEN
 } BlockInterfaceType;
 
 typedef struct DriveInfo {
diff --git a/vl.c b/vl.c
index 8aef3bd..7187545 100644
--- a/vl.c
+++ b/vl.c
@@ -5478,6 +5478,9 @@ static int drive_init(struct drive_opt *arg, int snapshot,
 	} else if (!strcmp(buf, "sd")) {
 	    type = IF_SD;
             max_devs = 0;
+	} 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;
@@ -5663,6 +5666,7 @@ static int drive_init(struct drive_opt *arg, int snapshot,
     switch(type) {
     case IF_IDE:
     case IF_SCSI:
+    case IF_XEN:
         switch(media) {
 	case MEDIA_DISK:
             if (cyls != 0) {
-- 
1.5.4.1

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 5/7] xen: add block device backend driver.
@ 2008-07-28 13:17   ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

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    |  681 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/xen-machine.c |    1 +
 sysemu.h         |    2 +-
 vl.c             |    4 +
 7 files changed, 793 insertions(+), 2 deletions(-)
 create mode 100644 hw/xen-blkif.h
 create mode 100644 hw/xen-disk.c

diff --git a/Makefile.target b/Makefile.target
index 66d41ee..2d599d2 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -517,7 +517,7 @@ endif
 
 # xen backend driver support
 XEN_OBJS := xen-machine.o xen-backend.o
-XEN_OBJS += xen-console.o xen-framebuffer.o
+XEN_OBJS += xen-console.o xen-framebuffer.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 3facf90..941b0a6 100644
--- a/hw/xen-backend.h
+++ b/hw/xen-backend.h
@@ -10,6 +10,7 @@
 
 #include "hw.h"
 #include "xen.h"
+#include "sysemu.h"
 
 /*
  * tweaks needed to build with different xen versions
@@ -118,6 +119,7 @@ void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...)
 struct devops xen_console_ops;      /* xen_console.c     */
 struct devops xen_kbdmouse_ops;     /* xen_framebuffer.c */
 struct devops xen_framebuffer_ops;  /* xen_framebuffer.c */
+struct devops xen_blkdev_ops;       /* xen_disk.c        */
 
 void xen_set_display(int domid, DisplayState *ds);
 
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..6947363
--- /dev/null
+++ b/hw/xen-disk.c
@@ -0,0 +1,681 @@
+/*
+ *  xen paravirt block device backend
+ *
+ *  FIXME: the code is designed to handle multiple outstanding
+ *  requests (using aio or using threads), which isn't used right
+ *  now due to limitations of the qemu block driver interface.
+ *
+ *
+ *  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 <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/utsname.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 "list.h"
+
+#include "hw.h"
+#include "block_int.h"
+#include "qemu-char.h"
+#include "xen-blkif.h"
+#include "xen-backend.h"
+
+/* ------------------------------------------------------------- */
+
+#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;
+
+    struct blkdev       *blkdev;
+    struct list_head    list;
+};
+
+struct blkdev {
+    struct xendev       xendev;  /* must be first */
+    char                *params;
+    char                *mode;
+    char                *type;
+    char                *dev;
+    char                *fileproto;
+    char                *filename;
+    int                 ring_ref;
+    void                *sring;
+    int                 file;
+    int64_t             file_blk;
+    int64_t             file_size;
+    int                 protocol;
+    blkif_back_rings_t  rings;
+    int                 more_work;
+    int                 cnt_map;
+
+    /* request lists */
+    struct list_head    inflight;
+    struct list_head    finished;
+    struct list_head    freelist;
+    int                 requests;
+
+    /* qemu block driver */
+    BlockDriverState    *bs;
+};
+
+static int syncwrite    = 0;
+static int batch_maps   = 0;
+static int max_requests = 32;
+
+/* ------------------------------------------------------------- */
+
+static struct ioreq *ioreq_start(struct blkdev *blkdev)
+{
+    struct ioreq *ioreq = NULL;
+
+    if (list_empty(&blkdev->freelist)) {
+	if (blkdev->requests >= max_requests)
+	    goto out;
+	/* allocate new struct */
+	ioreq = malloc(sizeof(*ioreq));
+	if (NULL == ioreq)
+	    goto out;
+	memset(ioreq, 0, sizeof(*ioreq));
+	ioreq->blkdev = blkdev;
+	blkdev->requests++;
+    } else {
+	/* get one from freelist */
+	ioreq = list_entry(blkdev->freelist.next, struct ioreq, list);
+	list_del(&ioreq->list);
+    }
+    list_add_tail(&ioreq->list, &blkdev->inflight);
+
+out:
+    return ioreq;
+}
+
+static void ioreq_finish(struct ioreq *ioreq)
+{
+    struct blkdev *blkdev = ioreq->blkdev;
+
+    list_del(&ioreq->list);
+    list_add_tail(&ioreq->list, &blkdev->finished);
+}
+
+static void ioreq_release(struct ioreq *ioreq)
+{
+    struct blkdev *blkdev = ioreq->blkdev;
+
+    list_del(&ioreq->list);
+    memset(ioreq, 0, sizeof(*ioreq));
+    ioreq->blkdev = blkdev;
+    list_add_tail(&ioreq->list, &blkdev->freelist);
+}
+
+/*
+ * translate request into iovec + start offset + end offset
+ * do sanity checks along the way
+ */
+static int ioreq_parse(struct ioreq *ioreq)
+{
+    struct blkdev *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 (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 (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(struct ioreq *ioreq)
+{
+    struct blkdev *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);
+    return 0;
+
+err:
+    ioreq->status = BLKIF_RSP_ERROR;
+    return -1;
+}
+
+static int blk_send_response_one(struct ioreq *ioreq)
+{
+    struct blkdev     *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 blkdev *blkdev)
+{
+    struct list_head *item, *safe;
+    struct ioreq *ioreq;
+    int send_notify = 0;
+
+    list_for_each_safe(item, safe, &blkdev->finished) {
+	ioreq = list_entry(item, struct ioreq, list);
+	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 blkdev *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 blkdev *blkdev)
+{
+    RING_IDX rc, rp;
+    struct ioreq *ioreq;
+
+    do {
+	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'. */
+
+	/* Limit #of requests we queue up for I/O so we ack requests
+	 * faster if busy.  Improves backend/frontend parallelism and
+	 * reduces evchn signaling. */
+	if (rp > rc + (max_requests >> 2)) {
+	    rp = rc + (max_requests >> 2);
+	    blkdev->more_work++;
+	}
+
+	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;
+	    }
+
+	    /* run i/o in qemu mode */
+	    ioreq_runio_qemu(ioreq);
+	    ioreq_finish(ioreq);
+	}
+	blk_send_response_all(blkdev);
+
+    } while (blkdev->more_work);
+}
+
+/* ------------------------------------------------------------- */
+
+static void blk_alloc(struct xendev *xendev)
+{
+    struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev);
+
+    INIT_LIST_HEAD(&blkdev->inflight);
+    INIT_LIST_HEAD(&blkdev->finished);
+    INIT_LIST_HEAD(&blkdev->freelist);
+}
+
+static int blk_init(struct xendev *xendev)
+{
+    struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev);
+    int mode, qflags, have_barriers, index, 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");
+
+    /* 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 | VDISK_REMOVABLE | VDISK_CDROM;
+    }
+
+    /* init qemu block driver */
+    index = (blkdev->xendev.dev - 202 * 256) / 16;
+    index = drive_get_index(IF_XEN, 0, index);
+    if (-1 == index) {
+	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 {
+	blkdev->bs = drives_table[index].bdrv;
+    }
+    blkdev->file_blk  = BLOCK_SIZE;
+    blkdev->file_size = bdrv_getlength(blkdev->bs);
+    if (blkdev->file_size < 0)
+	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 xendev *xendev)
+{
+    struct blkdev *blkdev = container_of(xendev, struct blkdev, 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 (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 xendev *xendev)
+{
+    struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev);
+
+    if (-1 != blkdev->file) {
+	close(blkdev->file);
+	blkdev->file = -1;
+    }
+    if (blkdev->bs) {
+	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 xendev *xendev)
+{
+    struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev);
+    struct list_head *item, *safe;
+    struct ioreq *ioreq;
+
+    list_for_each_safe(item, safe, &blkdev->freelist) {
+	ioreq = list_entry(item, struct ioreq, list);
+	list_del(&ioreq->list);
+	free(ioreq);
+    }
+
+    free(blkdev->params);
+    free(blkdev->mode);
+    return 0;
+}
+
+static void blk_event(struct xendev *xendev)
+{
+    struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev);
+    blk_handle_requests(blkdev);
+}
+
+struct devops xen_blkdev_ops = {
+    .size       = sizeof(struct blkdev),
+    .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.c b/hw/xen-machine.c
index c03cb53..1b647a2 100644
--- a/hw/xen-machine.c
+++ b/hw/xen-machine.c
@@ -61,6 +61,7 @@ static void xenpv_init(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_set_display(xen_domid, ds);
diff --git a/sysemu.h b/sysemu.h
index b12fae0..49e75b1 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -113,7 +113,7 @@ extern unsigned int nb_prom_envs;
 #endif
 
 typedef enum {
-    IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD
+    IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_XEN
 } BlockInterfaceType;
 
 typedef struct DriveInfo {
diff --git a/vl.c b/vl.c
index 8aef3bd..7187545 100644
--- a/vl.c
+++ b/vl.c
@@ -5478,6 +5478,9 @@ static int drive_init(struct drive_opt *arg, int snapshot,
 	} else if (!strcmp(buf, "sd")) {
 	    type = IF_SD;
             max_devs = 0;
+	} 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;
@@ -5663,6 +5666,7 @@ static int drive_init(struct drive_opt *arg, int snapshot,
     switch(type) {
     case IF_IDE:
     case IF_SCSI:
+    case IF_XEN:
         switch(media) {
 	case MEDIA_DISK:
             if (cyls != 0) {
-- 
1.5.4.1

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Qemu-devel] [PATCH 6/7] xen: add net backend driver.
  2008-07-28 13:17 ` Gerd Hoffmann
@ 2008-07-28 13:17   ` Gerd Hoffmann
  -1 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

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.c |    1 +
 hw/xen-nic.c     |  448 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 451 insertions(+), 1 deletions(-)
 create mode 100644 hw/xen-nic.c

diff --git a/Makefile.target b/Makefile.target
index 2d599d2..281d7fa 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -517,7 +517,7 @@ endif
 
 # xen backend driver support
 XEN_OBJS := xen-machine.o xen-backend.o
-XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o
+XEN_OBJS += xen-console.o xen-framebuffer.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 941b0a6..2db2c3c 100644
--- a/hw/xen-backend.h
+++ b/hw/xen-backend.h
@@ -120,6 +120,7 @@ struct devops xen_console_ops;      /* xen_console.c     */
 struct devops xen_kbdmouse_ops;     /* xen_framebuffer.c */
 struct devops xen_framebuffer_ops;  /* xen_framebuffer.c */
 struct devops xen_blkdev_ops;       /* xen_disk.c        */
+struct devops xen_netdev_ops;       /* xen_nic.c         */
 
 void xen_set_display(int domid, DisplayState *ds);
 
diff --git a/hw/xen-machine.c b/hw/xen-machine.c
index 1b647a2..3fa4079 100644
--- a/hw/xen-machine.c
+++ b/hw/xen-machine.c
@@ -62,6 +62,7 @@ static void xenpv_init(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_set_display(xen_domid, ds);
diff --git a/hw/xen-nic.c b/hw/xen-nic.c
new file mode 100644
index 0000000..56d1474
--- /dev/null
+++ b/hw/xen-nic.c
@@ -0,0 +1,448 @@
+/*
+ *  xen paravirt network card backend
+ *
+ *  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 netdev {
+    struct xendev         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;
+};
+
+/* ------------------------------------------------------------- */
+
+#define PROTO_TCP  6
+#define PROTO_UDP 17
+
+static uint32_t checksum_add(int len, uint8_t *buf)
+{
+    uint32_t sum = 0;
+    int i;
+
+    for (i = 0; i < len; i++) {
+	if (i & 1)
+	    sum += (uint32_t)buf[i];
+	else
+	    sum += (uint32_t)buf[i] << 8;
+    }
+    return sum;
+}
+
+static uint16_t checksum_finish(uint32_t sum)
+{
+    while (sum>>16)
+	sum = (sum & 0xFFFF)+(sum >> 16);
+    return ~sum;
+}
+
+static uint16_t checksum_tcpudp(uint16_t length, uint16_t proto,
+				uint8_t *addrs, uint8_t *buf)
+{
+    uint32_t sum = 0;
+
+    sum += checksum_add(length, buf);         // payload
+    sum += checksum_add(8, addrs);            // src + dst address
+    sum += proto + length;                    // protocol & length
+    return checksum_finish(sum);
+}
+
+static void checksum_calculate(uint8_t *data, int length)
+{
+    int hlen, plen, proto, csum_offset;
+    uint16_t csum;
+
+    if ((data[14] & 0xf0) != 0x40)
+	return; /* not IPv4 */
+    hlen  = (data[14] & 0x0f) * 4;
+    plen  = (data[16] << 8 | data[17]) - hlen;
+    proto = data[23];
+
+    switch (proto) {
+    case PROTO_TCP:
+	csum_offset = 16;
+	break;
+    case PROTO_UDP:
+	csum_offset = 6;
+	break;
+    default:
+	return;
+    }
+
+    if (plen < csum_offset+2)
+	return;
+
+    data[14+hlen+csum_offset]   = 0;
+    data[14+hlen+csum_offset+1] = 0;
+    csum = checksum_tcpudp(plen, proto, data+14+12, data+14+hlen);
+    data[14+hlen+csum_offset]   = csum >> 8;
+    data[14+hlen+csum_offset+1] = csum & 0xff;
+}
+
+
+/* ------------------------------------------------------------- */
+
+static void net_tx_response(struct netdev *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 netdev *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 netdev *netdev)
+{
+    netif_tx_request_t txreq;
+    RING_IDX rc, rp;
+    void *page;
+
+    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: gref dereference failed\n");
+		net_tx_error(netdev, &txreq, rc);
+		continue;
+	    }
+	    if (txreq.flags & NETTXF_csum_blank)
+		checksum_calculate(page + txreq.offset, txreq.size);
+	    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;
+    }
+}
+
+/* ------------------------------------------------------------- */
+
+static void net_rx_response(struct netdev *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 netdev *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 netdev *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: gref dereference failed\n");
+	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 xendev *xendev)
+{
+    struct netdev *netdev = container_of(xendev, struct netdev, 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, 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 xendev *xendev)
+{
+    struct netdev *netdev = container_of(xendev, struct netdev, 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 xendev *xendev)
+{
+    struct netdev *netdev = container_of(xendev, struct netdev, 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;
+    }
+}
+
+static void net_event(struct xendev *xendev)
+{
+    struct netdev *netdev = container_of(xendev, struct netdev, xendev);
+    net_tx_packets(netdev);
+}
+
+/* ------------------------------------------------------------- */
+
+struct devops xen_netdev_ops = {
+    .size       = sizeof(struct netdev),
+    .flags      = DEVOPS_FLAG_NEED_GNTDEV,
+    .init       = net_init,
+    .connect    = net_connect,
+    .event      = net_event,
+    .disconnect = net_disconnect,
+};
-- 
1.5.4.1

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 6/7] xen: add net backend driver.
@ 2008-07-28 13:17   ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

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.c |    1 +
 hw/xen-nic.c     |  448 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 451 insertions(+), 1 deletions(-)
 create mode 100644 hw/xen-nic.c

diff --git a/Makefile.target b/Makefile.target
index 2d599d2..281d7fa 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -517,7 +517,7 @@ endif
 
 # xen backend driver support
 XEN_OBJS := xen-machine.o xen-backend.o
-XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o
+XEN_OBJS += xen-console.o xen-framebuffer.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 941b0a6..2db2c3c 100644
--- a/hw/xen-backend.h
+++ b/hw/xen-backend.h
@@ -120,6 +120,7 @@ struct devops xen_console_ops;      /* xen_console.c     */
 struct devops xen_kbdmouse_ops;     /* xen_framebuffer.c */
 struct devops xen_framebuffer_ops;  /* xen_framebuffer.c */
 struct devops xen_blkdev_ops;       /* xen_disk.c        */
+struct devops xen_netdev_ops;       /* xen_nic.c         */
 
 void xen_set_display(int domid, DisplayState *ds);
 
diff --git a/hw/xen-machine.c b/hw/xen-machine.c
index 1b647a2..3fa4079 100644
--- a/hw/xen-machine.c
+++ b/hw/xen-machine.c
@@ -62,6 +62,7 @@ static void xenpv_init(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_set_display(xen_domid, ds);
diff --git a/hw/xen-nic.c b/hw/xen-nic.c
new file mode 100644
index 0000000..56d1474
--- /dev/null
+++ b/hw/xen-nic.c
@@ -0,0 +1,448 @@
+/*
+ *  xen paravirt network card backend
+ *
+ *  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 netdev {
+    struct xendev         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;
+};
+
+/* ------------------------------------------------------------- */
+
+#define PROTO_TCP  6
+#define PROTO_UDP 17
+
+static uint32_t checksum_add(int len, uint8_t *buf)
+{
+    uint32_t sum = 0;
+    int i;
+
+    for (i = 0; i < len; i++) {
+	if (i & 1)
+	    sum += (uint32_t)buf[i];
+	else
+	    sum += (uint32_t)buf[i] << 8;
+    }
+    return sum;
+}
+
+static uint16_t checksum_finish(uint32_t sum)
+{
+    while (sum>>16)
+	sum = (sum & 0xFFFF)+(sum >> 16);
+    return ~sum;
+}
+
+static uint16_t checksum_tcpudp(uint16_t length, uint16_t proto,
+				uint8_t *addrs, uint8_t *buf)
+{
+    uint32_t sum = 0;
+
+    sum += checksum_add(length, buf);         // payload
+    sum += checksum_add(8, addrs);            // src + dst address
+    sum += proto + length;                    // protocol & length
+    return checksum_finish(sum);
+}
+
+static void checksum_calculate(uint8_t *data, int length)
+{
+    int hlen, plen, proto, csum_offset;
+    uint16_t csum;
+
+    if ((data[14] & 0xf0) != 0x40)
+	return; /* not IPv4 */
+    hlen  = (data[14] & 0x0f) * 4;
+    plen  = (data[16] << 8 | data[17]) - hlen;
+    proto = data[23];
+
+    switch (proto) {
+    case PROTO_TCP:
+	csum_offset = 16;
+	break;
+    case PROTO_UDP:
+	csum_offset = 6;
+	break;
+    default:
+	return;
+    }
+
+    if (plen < csum_offset+2)
+	return;
+
+    data[14+hlen+csum_offset]   = 0;
+    data[14+hlen+csum_offset+1] = 0;
+    csum = checksum_tcpudp(plen, proto, data+14+12, data+14+hlen);
+    data[14+hlen+csum_offset]   = csum >> 8;
+    data[14+hlen+csum_offset+1] = csum & 0xff;
+}
+
+
+/* ------------------------------------------------------------- */
+
+static void net_tx_response(struct netdev *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 netdev *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 netdev *netdev)
+{
+    netif_tx_request_t txreq;
+    RING_IDX rc, rp;
+    void *page;
+
+    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: gref dereference failed\n");
+		net_tx_error(netdev, &txreq, rc);
+		continue;
+	    }
+	    if (txreq.flags & NETTXF_csum_blank)
+		checksum_calculate(page + txreq.offset, txreq.size);
+	    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;
+    }
+}
+
+/* ------------------------------------------------------------- */
+
+static void net_rx_response(struct netdev *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 netdev *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 netdev *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: gref dereference failed\n");
+	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 xendev *xendev)
+{
+    struct netdev *netdev = container_of(xendev, struct netdev, 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, 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 xendev *xendev)
+{
+    struct netdev *netdev = container_of(xendev, struct netdev, 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 xendev *xendev)
+{
+    struct netdev *netdev = container_of(xendev, struct netdev, 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;
+    }
+}
+
+static void net_event(struct xendev *xendev)
+{
+    struct netdev *netdev = container_of(xendev, struct netdev, xendev);
+    net_tx_packets(netdev);
+}
+
+/* ------------------------------------------------------------- */
+
+struct devops xen_netdev_ops = {
+    .size       = sizeof(struct netdev),
+    .flags      = DEVOPS_FLAG_NEED_GNTDEV,
+    .init       = net_init,
+    .connect    = net_connect,
+    .event      = net_event,
+    .disconnect = net_disconnect,
+};
-- 
1.5.4.1

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Qemu-devel] [PATCH 7/7] xen: blk & nic configuration via cmd line.
  2008-07-28 13:17 ` Gerd Hoffmann
@ 2008-07-28 13:17   ` Gerd Hoffmann
  -1 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

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.h |    7 +++
 hw/xen-config.c  |  140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/xen-machine.c |   22 ++++++++-
 4 files changed, 169 insertions(+), 2 deletions(-)
 create mode 100644 hw/xen-config.c

diff --git a/Makefile.target b/Makefile.target
index 281d7fa..acb679d 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -516,7 +516,7 @@ LIBS += $(CONFIG_VNC_TLS_LIBS)
 endif
 
 # xen backend driver support
-XEN_OBJS := xen-machine.o xen-backend.o
+XEN_OBJS := xen-machine.o xen-backend.o xen-config.o
 XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o xen-nic.o
 ifeq ($(CONFIG_XEN), yes)
   OBJS += $(XEN_OBJS)
diff --git a/hw/xen-backend.h b/hw/xen-backend.h
index 2db2c3c..2f21f84 100644
--- a/hw/xen-backend.h
+++ b/hw/xen-backend.h
@@ -11,6 +11,8 @@
 #include "hw.h"
 #include "xen.h"
 #include "sysemu.h"
+#include "net.h"
+#include "block_int.h"
 
 /*
  * tweaks needed to build with different xen versions
@@ -124,3 +126,8 @@ struct devops xen_netdev_ops;       /* xen_nic.c         */
 
 void xen_set_display(int domid, DisplayState *ds);
 
+/* configuration (aka xenbus setup) */
+void xen_config_cleanup(void);
+int xen_config_dev_blk(DriveInfo *disk);
+int xen_config_dev_nic(NICInfo *nic);
+
diff --git a/hw/xen-config.c b/hw/xen-config.c
new file mode 100644
index 0000000..112cfcf
--- /dev/null
+++ b/hw/xen-config.c
@@ -0,0 +1,140 @@
+#include "xen-backend.h"
+
+/* ------------------------------------------------------------- */
+
+struct xs_dirs {
+    char *xs_dir;
+    struct list_head list;
+};
+static LIST_HEAD (xs_cleanup);
+
+static int xen_config_cleanup_dir(char *dir)
+{
+    struct xs_dirs *d;
+
+    d = malloc(sizeof(*d));
+    d->xs_dir = dir;
+    list_add_tail(&d->list, &xs_cleanup);
+    return 0;
+}
+
+void xen_config_cleanup(void)
+{
+    struct list_head *item;
+    struct xs_dirs *d;
+
+    fprintf(stderr, "xen be: %s\n", __FUNCTION__);
+    list_for_each(item, &xs_cleanup) {
+	d = list_entry(item, struct xs_dirs, list);
+	xs_rm(xenstore, 0, d->xs_dir);
+    }
+}
+
+/* ------------------------------------------------------------- */
+
+static int xen_config_dev_mkdir(char *dev, int p)
+{
+    struct xs_permissions perms = {
+	.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(strdup(dev));
+
+    if (!xs_set_permissions(xenstore, 0, dev, &perms, 1)) {
+	fprintf(stderr, "%s: xs_set_permissions failed\n", __FUNCTION__);
+	return -1;
+    }
+    return 0;
+}
+
+static int xen_config_dev_dirs(char *ftype, char *btype, int vdev,
+			       char *fe, char *be, int len)
+{
+    snprintf(fe, len, "/local/domain/%d/device/%s/%d",
+	     xen_domid, ftype, vdev);
+    snprintf(be, len, "/local/domain/0/backend/%s/%d/%d",
+	     btype, xen_domid, vdev);
+
+    xen_config_dev_mkdir(fe, 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 0
+    xenstore_write_str(fe, "protocol",
+			    xen_config_dev_protocol(xen));
+#endif
+    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;
+    char *devtype = cdrom ? "cdrom" : "disk";
+    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.c b/hw/xen-machine.c
index 3fa4079..380a61b 100644
--- a/hw/xen-machine.c
+++ b/hw/xen-machine.c
@@ -43,6 +43,7 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
 		       const char *cpu_model)
 {
     CPUState *env;
+    int i, index;
 
     /* create dummy cpu, halted */
     if (cpu_model == NULL) {
@@ -55,9 +56,28 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
     env = cpu_init(cpu_model);
     env->halted = 1;
 
-    /* setup xen backend handlers */
+    /* backend core init */
     xen_be_init();
 
+    /* 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);
+
+    /* backend driver init */
     xen_be_register("console", &xen_console_ops);
     xen_be_register("vkbd", &xen_kbdmouse_ops);
     xen_be_register("vfb", &xen_framebuffer_ops);
-- 
1.5.4.1

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 7/7] xen: blk & nic configuration via cmd line.
@ 2008-07-28 13:17   ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 13:17 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

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.h |    7 +++
 hw/xen-config.c  |  140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/xen-machine.c |   22 ++++++++-
 4 files changed, 169 insertions(+), 2 deletions(-)
 create mode 100644 hw/xen-config.c

diff --git a/Makefile.target b/Makefile.target
index 281d7fa..acb679d 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -516,7 +516,7 @@ LIBS += $(CONFIG_VNC_TLS_LIBS)
 endif
 
 # xen backend driver support
-XEN_OBJS := xen-machine.o xen-backend.o
+XEN_OBJS := xen-machine.o xen-backend.o xen-config.o
 XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o xen-nic.o
 ifeq ($(CONFIG_XEN), yes)
   OBJS += $(XEN_OBJS)
diff --git a/hw/xen-backend.h b/hw/xen-backend.h
index 2db2c3c..2f21f84 100644
--- a/hw/xen-backend.h
+++ b/hw/xen-backend.h
@@ -11,6 +11,8 @@
 #include "hw.h"
 #include "xen.h"
 #include "sysemu.h"
+#include "net.h"
+#include "block_int.h"
 
 /*
  * tweaks needed to build with different xen versions
@@ -124,3 +126,8 @@ struct devops xen_netdev_ops;       /* xen_nic.c         */
 
 void xen_set_display(int domid, DisplayState *ds);
 
+/* configuration (aka xenbus setup) */
+void xen_config_cleanup(void);
+int xen_config_dev_blk(DriveInfo *disk);
+int xen_config_dev_nic(NICInfo *nic);
+
diff --git a/hw/xen-config.c b/hw/xen-config.c
new file mode 100644
index 0000000..112cfcf
--- /dev/null
+++ b/hw/xen-config.c
@@ -0,0 +1,140 @@
+#include "xen-backend.h"
+
+/* ------------------------------------------------------------- */
+
+struct xs_dirs {
+    char *xs_dir;
+    struct list_head list;
+};
+static LIST_HEAD (xs_cleanup);
+
+static int xen_config_cleanup_dir(char *dir)
+{
+    struct xs_dirs *d;
+
+    d = malloc(sizeof(*d));
+    d->xs_dir = dir;
+    list_add_tail(&d->list, &xs_cleanup);
+    return 0;
+}
+
+void xen_config_cleanup(void)
+{
+    struct list_head *item;
+    struct xs_dirs *d;
+
+    fprintf(stderr, "xen be: %s\n", __FUNCTION__);
+    list_for_each(item, &xs_cleanup) {
+	d = list_entry(item, struct xs_dirs, list);
+	xs_rm(xenstore, 0, d->xs_dir);
+    }
+}
+
+/* ------------------------------------------------------------- */
+
+static int xen_config_dev_mkdir(char *dev, int p)
+{
+    struct xs_permissions perms = {
+	.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(strdup(dev));
+
+    if (!xs_set_permissions(xenstore, 0, dev, &perms, 1)) {
+	fprintf(stderr, "%s: xs_set_permissions failed\n", __FUNCTION__);
+	return -1;
+    }
+    return 0;
+}
+
+static int xen_config_dev_dirs(char *ftype, char *btype, int vdev,
+			       char *fe, char *be, int len)
+{
+    snprintf(fe, len, "/local/domain/%d/device/%s/%d",
+	     xen_domid, ftype, vdev);
+    snprintf(be, len, "/local/domain/0/backend/%s/%d/%d",
+	     btype, xen_domid, vdev);
+
+    xen_config_dev_mkdir(fe, 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 0
+    xenstore_write_str(fe, "protocol",
+			    xen_config_dev_protocol(xen));
+#endif
+    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;
+    char *devtype = cdrom ? "cdrom" : "disk";
+    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.c b/hw/xen-machine.c
index 3fa4079..380a61b 100644
--- a/hw/xen-machine.c
+++ b/hw/xen-machine.c
@@ -43,6 +43,7 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
 		       const char *cpu_model)
 {
     CPUState *env;
+    int i, index;
 
     /* create dummy cpu, halted */
     if (cpu_model == NULL) {
@@ -55,9 +56,28 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
     env = cpu_init(cpu_model);
     env->halted = 1;
 
-    /* setup xen backend handlers */
+    /* backend core init */
     xen_be_init();
 
+    /* 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);
+
+    /* backend driver init */
     xen_be_register("console", &xen_console_ops);
     xen_be_register("vkbd", &xen_kbdmouse_ops);
     xen_be_register("vfb", &xen_framebuffer_ops);
-- 
1.5.4.1

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
  2008-07-28 13:17   ` Gerd Hoffmann
  (?)
@ 2008-07-28 14:04   ` Anthony Liguori
  2008-07-28 14:52     ` Gerd Hoffmann
  2008-07-28 23:14       ` Samuel Thibault
  -1 siblings, 2 replies; 65+ messages in thread
From: Anthony Liguori @ 2008-07-28 14:04 UTC (permalink / raw)
  To: qemu-devel; +Cc: xen-devel, Gerd Hoffmann

Gerd Hoffmann wrote:
> - configure script and build system changes.
> - wind up new machine type.
> - add -domid command line option.
> - allow xenpv machines run without disk and kernel specified.
>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  Makefile.target       |    8 +++++
>  configure             |   27 ++++++++++++++++
>  hw/boards.h           |    4 ++
>  hw/xen-machine.c      |   83 +++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/xen.h              |   12 +++++++
>  target-i386/machine.c |    3 ++
>  vl.c                  |   25 +++++++++++++--
>  7 files changed, 159 insertions(+), 3 deletions(-)
>  create mode 100644 hw/xen-machine.c
>  create mode 100644 hw/xen.h
>
> diff --git a/Makefile.target b/Makefile.target
> index ff105c1..4f42582 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -515,6 +515,14 @@ CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
>  LIBS += $(CONFIG_VNC_TLS_LIBS)
>  endif
>  
> +# xen backend driver support
> +XEN_OBJS := xen-machine.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 fd04766..34516b9 100755
> --- a/configure
> +++ b/configure
> @@ -108,6 +108,7 @@ uname_release=""
>  curses="yes"
>  nptl="yes"
>  mixemu="no"
> +xen="no"
>  
>  # OS specific
>  targetos=`uname -s`
> @@ -202,6 +203,7 @@ linux="yes"
>  linux_user="yes"
>  if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
>      kqemu="yes"
> +    xen="yes"
>      audio_possible_drivers="$audio_possible_drivers fmod"
>  fi
>  ;;
> @@ -285,6 +287,8 @@ for opt do
>    ;;
>    --disable-kqemu) kqemu="no"
>    ;;
> +  --disable-xen) xen="no"
> +  ;;
>    --disable-brlapi) brlapi="no"
>    ;;
>    --enable-profiler) profiler="yes"
> @@ -421,6 +425,7 @@ echo "                           Available drivers: $audio_possible_drivers"
>  echo "  --audio-card-list=LIST   set list of additional emulated audio cards"
>  echo "                           Available cards: ac97 adlib cs4231a gus"
>  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-curses         disable curses output"
> @@ -681,6 +686,22 @@ else
>  fi
>  
>  ##########################################
> +# xen probe
> +
> +if test "$xen" = "yes" ; then
> +cat > $TMPC <<EOF
> +#include <xs.h>
> +#include <xenctrl.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
> @@ -916,6 +937,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" ] && \
> @@ -1167,6 +1189,11 @@ if test "$brlapi" = "yes" ; then
>    echo "#define CONFIG_BRLAPI 1" >> $config_h
>    echo "BRLAPI_LIBS=-lbrlapi" >> $config_mak
>  fi
> +if test "$xen" = "yes" ; then
> +  echo "CONFIG_XEN=yes" >> $config_mak
> +  echo "#define CONFIG_XEN 1" >> $config_h
> +  echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak
> +fi
>  
>  # XXX: suppress that
>  if [ "$bsd" = "yes" ] ; then
> diff --git a/hw/boards.h b/hw/boards.h
> index 22ac332..5931720 100644
> --- a/hw/boards.h
> +++ b/hw/boards.h
> @@ -29,6 +29,10 @@ extern QEMUMachine bareetraxfs_machine;
>  extern QEMUMachine pc_machine;
>  extern QEMUMachine isapc_machine;
>  
> +/* xen_machine.c */
> +extern QEMUMachine xenpv_machine;
> +extern QEMUMachine xenfv_machine;
>   

Why does xenfv need its own machine type?

>  /* ppc.c */
>  extern QEMUMachine prep_machine;
>  extern QEMUMachine core99_machine;
> diff --git a/hw/xen-machine.c b/hw/xen-machine.c
> new file mode 100644
> index 0000000..88f0f6e
> --- /dev/null
> +++ b/hw/xen-machine.c
> @@ -0,0 +1,83 @@
> +/*
> + * QEMU Xen PV Machine
> + *
> + * Copyright (c) 2007,08 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 "boards.h"
> +
> +#include "xen.h"
> +
> +/* -------------------------------------------------------------------- */
> +/* variables                                                            */
> +
> +int xen_domid;
> +
> +/* -------------------------------------------------------------------- */
> +/* paravirtualized xen guests                                           */
> +
> +static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
> +		       const char *boot_device, DisplayState *ds,
> +		       const char *kernel_filename,
> +		       const char *kernel_cmdline,
> +		       const char *initrd_filename,
> +		       const char *cpu_model)
> +{
> +    CPUState *env;
> +
> +    /* create dummy cpu, halted */
> +    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 = {
> +    "xenpv",
> +    "paravirtualized Xen machine",
> +    xenpv_init,
> +};
> +
> +/* -------------------------------------------------------------------- */
> +/* fully virtualized xen guests                                         */
> +
> +static void xenfv_init(ram_addr_t ram_size, int vga_ram_size,
> +		       const char *boot_device, DisplayState *ds,
> +		       const char *kernel_filename,
> +		       const char *kernel_cmdline,
> +		       const char *initrd_filename,
> +		       const char *cpu_model)
> +{
> +    /* to be done */
> +    fprintf(stderr, "%s: not implemented yet\n", __FUNCTION__);
> +}
> +
> +QEMUMachine xenfv_machine = {
> +    "xenfv",
> +    "fully virtualized Xen machine",
> +    xenfv_init,
> +};
> diff --git a/hw/xen.h b/hw/xen.h
> new file mode 100644
> index 0000000..19349b5
> --- /dev/null
> +++ b/hw/xen.h
> @@ -0,0 +1,12 @@
> +/*
> + * public xen header
> + *   stuff needed outside xen-*.c, i.e. interfaces to qemu.
> + *   should not depend on any xen headers being present in
> + *   /usr/include/xen, so it can be included unconditionally.
> + *
> + * internal bits for the xen backend drivers are in xen-backend.h
> + */
> +
> +/* xen-machine.c */
> +extern int xen_domid;
> +
> diff --git a/target-i386/machine.c b/target-i386/machine.c
> index 91dbd55..98ece17 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 8801615..8aef3bd 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -29,6 +29,7 @@
>  #include "hw/audiodev.h"
>  #include "hw/isa.h"
>  #include "hw/baum.h"
> +#include "hw/xen.h"
>  #include "net.h"
>  #include "console.h"
>  #include "sysemu.h"
> @@ -7737,6 +7738,9 @@ static void help(int exitcode)
>             "-startdate      select initial date of the clock\n"
>             "-icount [N|auto]\n"
>             "                Enable virtual instruction counter with 2^N clock ticks per instruction\n"
> +#ifdef CONFIG_XEN
> +	   "-domid          specify xen guest domain id\n"
> +#endif
>             "\n"
>             "During emulation, the following keys are useful:\n"
>             "ctrl-alt-f      toggle full screen\n"
> @@ -7842,6 +7846,9 @@ enum {
>      QEMU_OPTION_startdate,
>      QEMU_OPTION_tb_size,
>      QEMU_OPTION_icount,
> +#ifdef CONFIG_XEN
> +    QEMU_OPTION_domid,
> +#endif
>  };
>  
>  typedef struct QEMUOption {
> @@ -7930,6 +7937,9 @@ const QEMUOption qemu_options[] = {
>  #ifdef CONFIG_CURSES
>      { "curses", 0, QEMU_OPTION_curses },
>  #endif
> +#ifdef CONFIG_XEN
> +    { "domid", HAS_ARG, QEMU_OPTION_domid },
> +#endif
>  
>      /* temporary options */
>      { "usb", 0, QEMU_OPTION_usb },
> @@ -8150,7 +8160,7 @@ int main(int argc, char **argv)
>  #endif
>      uint32_t boot_devices_bitmap = 0;
>      int i;
> -    int snapshot, linux_boot, net_boot;
> +    int snapshot, linux_boot, net_boot, nodisk_ok;
>      const char *initrd_filename;
>      const char *kernel_filename, *kernel_cmdline;
>      const char *boot_devices = "";
> @@ -8787,6 +8797,11 @@ int main(int argc, char **argv)
>                      icount_time_shift = strtol(optarg, NULL, 0);
>                  }
>                  break;
> +#ifdef CONFIG_XEN
> +            case QEMU_OPTION_domid:
> +                xen_domid = atoi(optarg);
> +                break;
> +#endif
>              }
>          }
>      }
> @@ -8852,9 +8867,13 @@ int main(int argc, char **argv)
>      linux_boot = (kernel_filename != NULL);
>      net_boot = (boot_devices_bitmap >> ('n' - 'a')) & 0xF;
>  
> -    /* XXX: this should not be: some embedded targets just have flash */
> +    /* need a disk for this machine to boot ? */
> +    /* XXX: add embedded targets which just have flash */
> +    nodisk_ok = 0;
> +    if (0 == strcmp(machine->name, "xenpv"))
> +	nodisk_ok = 1;
>      if (!linux_boot && net_boot == 0 &&
> -        nb_drives_opt == 0)
> +        !nodisk_ok && nb_drives_opt == 0)
>          help(1);

This patch is pretty clean with the exception of this bit.  I think a 
cleaner way to do this would be to let the machine types specify whether 
a disk is needed or not.

Regards,

Anthony LIguori

>  
>      if (!linux_boot && *kernel_cmdline != '\0') {
>   

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 2/7] xen: backend driver core
  2008-07-28 13:17   ` Gerd Hoffmann
  (?)
@ 2008-07-28 14:13   ` Anthony Liguori
  2008-07-28 15:51     ` Gerd Hoffmann
  -1 siblings, 1 reply; 65+ messages in thread
From: Anthony Liguori @ 2008-07-28 14:13 UTC (permalink / raw)
  To: qemu-devel; +Cc: xen-devel, Gerd Hoffmann

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/list.h        |  169 ++++++++++++++
>  hw/xen-backend.c |  663 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/xen-backend.h |  116 ++++++++++
>  hw/xen-machine.c |    5 +-
>  5 files changed, 953 insertions(+), 2 deletions(-)
>  create mode 100644 hw/list.h
>  create mode 100644 hw/xen-backend.c
>  create mode 100644 hw/xen-backend.h
>
> diff --git a/Makefile.target b/Makefile.target
> index 4f42582..0451048 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -516,7 +516,7 @@ LIBS += $(CONFIG_VNC_TLS_LIBS)
>  endif
>  
>  # xen backend driver support
> -XEN_OBJS := xen-machine.o
> +XEN_OBJS := xen-machine.o xen-backend.o
>  ifeq ($(CONFIG_XEN), yes)
>    OBJS += $(XEN_OBJS)
>    LIBS += $(XEN_LIBS)
> diff --git a/hw/list.h b/hw/list.h
> new file mode 100644
> index 0000000..fa9f790
> --- /dev/null
> +++ b/hw/list.h
>   

There's already a list implementation in audio/sys-queue.h

> diff --git a/hw/xen-backend.c b/hw/xen-backend.c
> new file mode 100644
> index 0000000..4819624
> --- /dev/null
> +++ b/hw/xen-backend.c
> @@ -0,0 +1,663 @@
> +/*
> + *  xen backend driver infrastructure
> + *
> + *  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 <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 LIST_HEAD(xendevs);
> +static const char *root = "/local/domain/0/backend";
> +static int debug = 0;
> +
> +/* ------------------------------------------------------------- */
>   

I think it would be good to provide some more commenting in this file.  
Just a high level description of XenStore and how all of this stuff fits 
together would be nice.

> +int xenstore_write_str(const char *base, const char *node, const char *val)
> +{
> +    char abspath[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[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;
> +    free(val);
> +    return rc;
> +}
> +
> +int xenstore_write_be_str(struct xendev *xendev, const char *node, const char *val)
> +{
> +    return xenstore_write_str(xendev->be, node, val);
> +}
> +
> +int xenstore_write_be_int(struct xendev *xendev, const char *node, int ival)
> +{
> +    return xenstore_write_int(xendev->be, node, ival);
> +}
> +
> +char *xenstore_read_be_str(struct xendev *xendev, const char *node)
> +{
> +    return xenstore_read_str(xendev->be, node);
> +}
> +
> +int xenstore_read_be_int(struct xendev *xendev, const char *node, int *ival)
> +{
> +    return xenstore_read_int(xendev->be, node, ival);
> +}
> +
> +char *xenstore_read_fe_str(struct xendev *xendev, const char *node)
> +{
> +    return xenstore_read_str(xendev->fe, node);
> +}
> +
> +int xenstore_read_fe_int(struct xendev *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 xendev *xendev, enum xenbus_state state)
> +{
> +    int rc;
> +
> +    rc = xenstore_write_be_int(xendev, "state", state);
> +    if (0 != rc)
>   

It's a nit, but try to avoid the defensive ifs.

> +	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 xendev *xen_be_find_xendev(char *type, int dom, int dev)
> +{
> +    struct xendev *xendev;
> +    struct list_head *item;
> +
> +    list_for_each(item, &xendevs) {
> +	xendev = list_entry(item, struct xendev, 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 xendev *xen_be_get_xendev(char *type, int dom, int dev,
> +					struct devops *ops)
> +{
> +    struct xendev *xendev;
> +
> +    xendev = xen_be_find_xendev(type, dom, dev);
> +    if (xendev)
> +	return xendev;
> +
> +    /* init new xendev */
> +    xendev = malloc(ops->size);
> +    memset(xendev,0,ops->size);
>   

qemu_mallocz()

> +    xendev->type  = type;
> +    xendev->dom   = dom;
> +    xendev->dev   = dev;
> +    xendev->ops   = ops;
> +    snprintf(xendev->be, sizeof(xendev->be), "%s/%s/%d/%d",
> +	     root, xendev->type, xendev->dom, xendev->dev);
> +    snprintf(xendev->name, sizeof(xendev->name), "%s-%d",
> +	     xendev->type, xendev->dev);
> +
> +    xendev->debug      = debug;
> +    xendev->local_port = -1;
> +
> +    xendev->evtchndev = xc_evtchn_open();
> +    if (xendev->evtchndev < 0) {
> +	fprintf(stderr, "can't open evtchn device\n");
> +	free(xendev);
>   

qemu_free()

Considering all you're doing, the patch looks pretty nice.

Regards,

Anthony Liguori

> +	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);
> +	    free(xendev);
> +	    return NULL;
> +	}
> +    } else {
> +	xendev->gnttabdev = -1;
> +    }
> +
> +    list_add_tail(&xendev->next, &xendevs);
> +
> +    if (xendev->ops->alloc)
> +	xendev->ops->alloc(xendev);
> +
> +    return xendev;
> +}
> +
> +/*
> + * release xen backend device.
> + */
> +static struct xendev *xen_be_del_xendev(int dom, int dev)
> +{
> +    struct xendev *xendev;
> +    struct list_head *item, *tmp;
> +
> +    list_for_each_safe(item, tmp, &xendevs) {
> +	xendev = list_entry(item, struct xendev, 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[BUFSIZE];
> +	    snprintf(token, sizeof(token), "fe:%p", xendev);
> +	    xs_unwatch(xenstore, xendev->fe, token);
> +	    free(xendev->fe);
> +	}
> +
> +	if (xendev->evtchndev >= 0)
> +	    xc_evtchn_close(xendev->evtchndev);
> +	if (xendev->gnttabdev >= 0)
> +	    xc_gnttab_close(xendev->gnttabdev);
> +
> +	list_del(&xendev->next);
> +	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 xendev *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 xendev *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")) {
> +	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 xendev *xendev)
> +{
> +    char token[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 xendev *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 xendev *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);
> +
> +#if 0
> +    if (xendev->ops->event)
> +	xendev->ops->event(xendev);
> +#endif
> +    return 0;
> +}
> +
> +/*
> + * Teardown connection.
> + *
> + * Goes to Closed when done.
> + */
> +static void xen_be_disconnect(struct xendev *xendev)
> +{
> +    if (xendev->be_state == XenbusStateClosed)
> +	return;
> +    if (xendev->ops->disconnect)
> +	xendev->ops->disconnect(xendev);
> +    xen_be_set_state(xendev, XenbusStateClosed);
> +}
> +
> +/*
> + * state change dispatcher function
> + */
> +void xen_be_check_state(struct xendev *xendev)
> +{
> +    int rc = 0;
> +
> +    /* frontend may request shutdown from almost anywhere */
> +    if (xendev->fe_state == XenbusStateClosing ||
> +	xendev->fe_state == XenbusStateClosed) {
> +	xen_be_disconnect(xendev);
> +	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;
> +	default:
> +	    rc = -1;
> +	}
> +	if (0 != rc)
> +	    break;
> +    }
> +}
> +
> +/* ------------------------------------------------------------- */
> +
> +static int xenstore_scan(char *type, int dom, struct devops *ops)
> +{
> +    struct xendev *xendev;
> +    char path[BUFSIZE], token[BUFSIZE];
> +    char **dev = NULL;
> +    unsigned int cdev, j;
> +
> +    /* setup watch */
> +    snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops);
> +    snprintf(path, sizeof(path), "%s/%s/%d", root, type, dom);
> +    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);
> +    }
> +    free(dev);
> +    return 0;
> +}
> +
> +static void xenstore_update_be(char *watch, char *type, int dom,
> +			       struct devops *ops)
> +{
> +    struct xendev *xendev;
> +    char path[BUFSIZE];
> +    unsigned int len, dev;
> +
> +    len = snprintf(path, sizeof(path), "%s/%s/%d", root, type, dom);
> +    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 xendev *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:
> +    free(vec);
> +}
> +
> +static void xen_be_evtchn_event(void *opaque)
> +{
> +    struct xendev *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(char *type, struct devops *ops)
> +{
> +    return xenstore_scan(type, xen_domid, ops);
> +}
> +
> +int xen_be_bind_evtchn(struct xendev *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) {
> +	xendev->local_port = 0;
> +	return -1;
> +    }
> +    qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev),
> +			xen_be_evtchn_event, NULL, xendev);
> +#if 0
> +    xc_evtchn_unmask(xendev->evtchndev, xendev->local_port);
> +#endif
> +    return 0;
> +}
> +
> +void xen_be_unbind_evtchn(struct xendev *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);
> +    xendev->local_port = -1;
> +}
> +
> +int xen_be_send_notify(struct xendev *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 xendev *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..db36ae7
> --- /dev/null
> +++ b/hw/xen-backend.h
> @@ -0,0 +1,116 @@
> +#include <stddef.h>
> +#include <xs.h>
> +#include <xenctrl.h>
> +#include <xen/elfnote.h>
> +#include <xen/elfstructs.h>
> +
> +#include <xen/io/xenbus.h>
> +
> +#include "list.h"
> +
> +#include "hw.h"
> +#include "xen.h"
> +
> +/*
> + * 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__ < 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
> +
> +/* ------------------------------------------------------------- */
> +
> +#define container_of(ptr, type, member) ({                      \
> +        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
> +        (type *)( (char *)__mptr - offsetof(type,member) );})
> +
> +/* ------------------------------------------------------------- */
> +
> +#define BUFSIZE 1024
> +
> +struct xendev;
> +
> +/* 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 devops {
> +    size_t    size;
> +    uint32_t  flags;
> +    void      (*alloc)(struct xendev *xendev);
> +    int       (*init)(struct xendev *xendev);
> +    int       (*connect)(struct xendev *xendev);
> +    void      (*event)(struct xendev *xendev);
> +    void      (*disconnect)(struct xendev *xendev);
> +    int       (*free)(struct xendev *xendev);
> +    void      (*backend_changed)(struct xendev *xendev, const char *node);
> +    void      (*frontend_changed)(struct xendev *xendev, const char *node);
> +};
> +
> +struct xendev {
> +    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[BUFSIZE];
> +    char               *fe;
> +    char               *protocol;
> +    int                remote_port;
> +    int                local_port;
> +
> +    int                evtchndev;
> +    int                gnttabdev;
> +
> +    struct devops      *ops;
> +    struct list_head   next;
> +};
> +
> +/* ------------------------------------------------------------- */
> +
> +/* variables */
> +extern int xen_domid; /* set by cmd line option, in vl.c */
> +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 xendev *xendev, const char *node, const char *val);
> +int xenstore_write_be_int(struct xendev *xendev, const char *node, int ival);
> +char *xenstore_read_be_str(struct xendev *xendev, const char *node);
> +int xenstore_read_be_int(struct xendev *xendev, const char *node, int *ival);
> +char *xenstore_read_fe_str(struct xendev *xendev, const char *node);
> +int xenstore_read_fe_int(struct xendev *xendev, const char *node, int *ival);
> +
> +const char *xenbus_strstate(enum xenbus_state state);
> +struct xendev *xen_be_find_xendev(char *type, int dom, int dev);
> +void xen_be_check_state(struct xendev *xendev);
> +
> +/* xen backend driver bits */
> +int xen_be_init(void);
> +int xen_be_register(char *type, struct devops *ops);
> +int xen_be_set_state(struct xendev *xendev, enum xenbus_state state);
> +int xen_be_bind_evtchn(struct xendev *xendev);
> +void xen_be_unbind_evtchn(struct xendev *xendev);
> +int xen_be_send_notify(struct xendev *xendev);
> +void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...)
> +    __attribute__ ((format(printf, 3, 4)));
> +
> diff --git a/hw/xen-machine.c b/hw/xen-machine.c
> index 88f0f6e..798c0a7 100644
> --- a/hw/xen-machine.c
> +++ b/hw/xen-machine.c
> @@ -25,7 +25,7 @@
>  #include "hw.h"
>  #include "boards.h"
>  
> -#include "xen.h"
> +#include "xen-backend.h"
>  
>  /* -------------------------------------------------------------------- */
>  /* variables                                                            */
> @@ -54,6 +54,9 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
>      }
>      env = cpu_init(cpu_model);
>      env->halted = 1;
> +
> +    /* setup xen backend handlers */
> +    xen_be_init();
>  }
>  
>  QEMUMachine xenpv_machine = {
>   

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 3/7] xen: add console backend driver.
  2008-07-28 13:17   ` Gerd Hoffmann
  (?)
@ 2008-07-28 14:17   ` Anthony Liguori
  2008-07-28 15:43     ` Gerd Hoffmann
  -1 siblings, 1 reply; 65+ messages in thread
From: Anthony Liguori @ 2008-07-28 14:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: xen-devel, Gerd Hoffmann

Gerd Hoffmann wrote:
> 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 |  271 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/xen-machine.c |    2 +
>  4 files changed, 277 insertions(+), 0 deletions(-)
>  create mode 100644 hw/xen-console.c
>
> diff --git a/Makefile.target b/Makefile.target
> index 0451048..05619fa 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -517,6 +517,7 @@ endif
>  
>  # xen backend driver support
>  XEN_OBJS := xen-machine.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 db36ae7..55ffd31 100644
> --- a/hw/xen-backend.h
> +++ b/hw/xen-backend.h
> @@ -114,3 +114,6 @@ int xen_be_send_notify(struct xendev *xendev);
>  void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...)
>      __attribute__ ((format(printf, 3, 4)));
>  
> +/* actual backend drivers */
> +struct devops xen_console_ops;      /* xen_console.c     */
> +
> diff --git a/hw/xen-console.c b/hw/xen-console.c
> new file mode 100644
> index 0000000..9c67f1b
> --- /dev/null
> +++ b/hw/xen-console.c
> @@ -0,0 +1,271 @@
> +/*
> + *  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 <malloc.h>
> +#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 xen_console {
> +    struct xendev     xendev;  /* must be first */
> +    struct buffer     buffer;
> +    char              console[BUFSIZE];
> +    int               ring_ref;
> +    void              *sring;
> +    CharDriverState   *chr;
> +    int               backlog;
> +};
> +
> +static void buffer_append(struct xen_console *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 = 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 = 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 xen_console *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 xen_console *con = opaque;
> +    return ring_free_bytes(con);
> +}
> +
> +static void xencons_receive(void *opaque, const uint8_t *buf, int len)
> +{
> +    struct xen_console *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 xen_console *con)
> +{
> +    ssize_t len, size;
> +
> +    size = con->buffer.size - con->buffer.consumed;
> +    len = qemu_chr_write(con->chr, con->buffer.data + con->buffer.consumed,
> +			 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 xendev *xendev)
> +{
> +    struct xen_console *con = container_of(xendev, struct xen_console, xendev);
> +    char *type;
> +
> +    if (!serial_hds[con->xendev.dev]) {
> +	xen_be_printf(xendev, 1, "serial line %d not configured\n", con->xendev.dev);
> +	return -1;
> +    }
> +
> +    /* setup */
> +    snprintf(con->console, sizeof(con->console),
> +	     "/local/domain/%d/console", con->xendev.dom);
> +    con->chr = serial_hds[con->xendev.dev];
> +
> +    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;
> +    }
> +
> +    return 0;
> +}
> +
> +static int con_connect(struct xendev *xendev)
> +{
> +    struct xen_console *con = container_of(xendev, struct xen_console, 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);
> +    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 xendev *xendev)
> +{
> +    struct xen_console *con = container_of(xendev, struct xen_console, xendev);
> +
> +    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 xendev *xendev)
> +{
> +    struct xen_console *con = container_of(xendev, struct xen_console, xendev);
> +
> +    buffer_append(con);
> +    if (con->buffer.size - con->buffer.consumed)
> +	xencons_send(con);
> +}
> +
> +/* -------------------------------------------------------------------- */
> +
> +struct devops xen_console_ops = {
>   

I missed it in the previous patchset, but it would be nice to QEMU-ify 
these things.  For instance, 'struct xen_console' => 'XenConsole', 
'struct devopts' => 'XenDevOpts', etc.

Regards,

Anthony Liguori

> +    .size       = sizeof(struct xen_console),
> +    .flags      = DEVOPS_FLAG_IGNORE_STATE,
> +    .init       = con_init,
> +    .connect    = con_connect,
> +    .event      = con_event,
> +    .disconnect = con_disconnect,
> +};
> diff --git a/hw/xen-machine.c b/hw/xen-machine.c
> index 798c0a7..da10982 100644
> --- a/hw/xen-machine.c
> +++ b/hw/xen-machine.c
> @@ -57,6 +57,8 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
>  
>      /* setup xen backend handlers */
>      xen_be_init();
> +
> +    xen_be_register("console", &xen_console_ops);
>  }
>  
>  QEMUMachine xenpv_machine = {
>   

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
  2008-07-28 13:17   ` Gerd Hoffmann
  (?)
@ 2008-07-28 14:22   ` Anthony Liguori
  2008-07-28 14:41       ` Andreas Färber
  2008-07-30  9:20       ` Gerd Hoffmann
  -1 siblings, 2 replies; 65+ messages in thread
From: Anthony Liguori @ 2008-07-28 14:22 UTC (permalink / raw)
  To: qemu-devel; +Cc: xen-devel, Gerd Hoffmann

Gerd Hoffmann wrote:
> 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 unstable (aka son-to-be 3.3).
>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  Makefile.target      |    2 +-
>  hw/xen-backend.h     |    4 +
>  hw/xen-framebuffer.c |  925 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/xen-machine.c     |    5 +
>  4 files changed, 935 insertions(+), 1 deletions(-)
>  create mode 100644 hw/xen-framebuffer.c
>
> diff --git a/Makefile.target b/Makefile.target
> index 05619fa..66d41ee 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -517,7 +517,7 @@ endif
>  
>  # xen backend driver support
>  XEN_OBJS := xen-machine.o xen-backend.o
> -XEN_OBJS += xen-console.o
> +XEN_OBJS += xen-console.o xen-framebuffer.o
>  ifeq ($(CONFIG_XEN), yes)
>    OBJS += $(XEN_OBJS)
>    LIBS += $(XEN_LIBS)
> diff --git a/hw/xen-backend.h b/hw/xen-backend.h
> index 55ffd31..3facf90 100644
> --- a/hw/xen-backend.h
> +++ b/hw/xen-backend.h
> @@ -116,4 +116,8 @@ void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...)
>  
>  /* actual backend drivers */
>  struct devops xen_console_ops;      /* xen_console.c     */
> +struct devops xen_kbdmouse_ops;     /* xen_framebuffer.c */
> +struct devops xen_framebuffer_ops;  /* xen_framebuffer.c */
> +
> +void xen_set_display(int domid, DisplayState *ds);
>  
> diff --git a/hw/xen-framebuffer.c b/hw/xen-framebuffer.c
> new file mode 100644
> index 0000000..716eafd
> --- /dev/null
> +++ b/hw/xen-framebuffer.c
> @@ -0,0 +1,925 @@
> +/*
> + *  xen paravirt framebuffer backend
>
>   

No copyright?

> + *  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 xen_common {
> +    struct xendev     xendev;  /* must be first */
> +    void              *page;
> +    DisplayState      *ds;
> +};
> +
> +struct xen_input {
> +    struct xen_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 xen_fb {
> +    struct xen_common c;
> +    size_t            fb_len;
> +    int               row_stride;
> +    int               depth;
> +    int               width;
> +    int               height;
> +    int               offset;
> +    void              *pixels;
> +    int               feature_update;
> +    int               refresh_period;
> +    int               bug_trigger;
> +
> +    struct {
> +	int x,y,w,h;
> +    } up_rects[UP_QUEUE];
> +    int               up_count;
> +    int               up_fullscreen;
> +};
> +
> +/* -------------------------------------------------------------------- */
> +
> +static int common_bind(struct xen_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;
> +
> +    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);
> +
> +    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;
> +    return 0;
> +}
> +
> +static void common_unbind(struct xen_common *c)
> +{
> +    if (c->page) {
> +	munmap(c->page, XC_PAGE_SIZE);
> +	c->page = NULL;
> +    }
> +    xen_be_unbind_evtchn(&c->xendev);
> +}
> +
> +/* -------------------------------------------------------------------- */
> +
> +/*
> + * 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
> +
> +};
> +
> +static unsigned char scancode2linux[512];
> +
> +/* Send an event to the keyboard frontend driver */
> +static int xenfb_kbd_event(struct xen_input *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;
> +
> +    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 xen_input *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 xen_input *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 xen_input *xenfb,
> +			       int abs_x, int abs_y, int rel_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.rel_z = rel_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 xen_input *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 xen_input *xenfb = opaque;
> +    int i;
> +
> +    if (xenfb->abs_pointer_wanted)
> +	xenfb_send_position(xenfb,
> +			    dx * (xenfb->c.ds->width - 1) / 0x7fff,
> +			    dy * (xenfb->c.ds->height - 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 xendev *xendev)
> +{
> +    struct xen_input *in = container_of(xendev, struct xen_input, c.xendev);
> +    static int first = 1;
> +    int i;
> +
> +    if (first) {
> +	/* Prepare scancode mapping table */
> +	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];
> +	}
> +	first = 0;
> +    }
> +
> +    if (!in->c.ds) {
> +        /* xen_set_display() below will set that and trigger us again */
> +        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 xendev *xendev)
> +{
> +    struct xen_input *in = container_of(xendev, struct xen_input, 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 xendev *xendev)
> +{
> +    struct xen_input *in = container_of(xendev, struct xen_input, 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 xendev *xendev)
> +{
> +    struct xen_input *xenfb = container_of(xendev, struct xen_input, 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 xen_fb *xenfb)
> +{
> +    struct xenfb_page *page = xenfb->c.page;
> +    char *protocol = xenfb->c.xendev.protocol;
> +    int n_fbmfns;
> +    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
> +    }
> +
> +    n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
> +    n_fbdirs = n_fbmfns * mode / 8;
> +    n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
> +
> +    pgmfns = malloc(sizeof(unsigned long) * n_fbdirs);
> +    fbmfns = malloc(sizeof(unsigned long) * n_fbmfns);
> +    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, n_fbmfns, 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, n_fbmfns);
> +    if (xenfb->pixels == NULL)
> +	goto out;
> +
> +    ret = 0; /* all is fine */
> +
> +out:
> +    if (pgmfns)
> +	free(pgmfns);
> +    if (fbmfns)
> +	free(fbmfns);
> +    return ret;
> +}
> +
> +static int xenfb_configure_fb(struct xen_fb *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;
> +    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 *)(xenfb->c.ds->data				\
> +			       + (line * xenfb->c.ds->linesize)		\
> +			       + (x * xenfb->c.ds->depth / 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 + xenfb->c.ds->depth / 8); \
> +	}								\
> +    }
>   

There are similar conversion macros in the VGA code.  I don't know how 
practical it would be to reuse them but it's at least worth looking at.

Regards,

Anthony Liguori

> +
> +/* This copies data from the guest framebuffer region, into QEMU's copy
> + * NB. QEMU's copy is stored in the pixel format of a) the local X
> + * server (SDL case) or b) the current VNC client pixel format.
> + * When shifting between colour depths we preserve the MSB.
> + */
> +static void xenfb_guest_copy(struct xen_fb *xenfb, int x, int y, int w, int h)
> +{
> +    int line;
> +
> +    if (1 /* !xenfb->c.ds->shared_buf */) {
> +	if (xenfb->depth == xenfb->c.ds->depth) { /* Perfect match can use fast path */
> +	    for (line = y ; line < (y+h) ; line++) {
> +		memcpy(xenfb->c.ds->data + (line * xenfb->c.ds->linesize) + (x * xenfb->c.ds->depth / 8),
> +		       xenfb->pixels + xenfb->offset + (line * xenfb->row_stride) + (x * xenfb->depth / 8),
> +		       w * xenfb->depth / 8);
> +	    }
> +	} else { /* Mismatch requires slow pixel munging */
> +	    /* 8 bit == r:3 g:3 b:2 */
> +	    /* 16 bit == r:5 g:6 b:5 */
> +	    /* 24 bit == r:8 g:8 b:8 */
> +	    /* 32 bit == r:8 g:8 b:8 (padding:8) */
> +	    if (xenfb->depth == 8) {
> +		if (xenfb->c.ds->depth == 16) {
> +		    BLT(uint8_t, uint16_t,   3, 3, 2,   5, 6, 5);
> +		} else if (xenfb->c.ds->depth == 32) {
> +		    BLT(uint8_t, uint32_t,   3, 3, 2,   8, 8, 8);
> +		}
> +	    } else if (xenfb->depth == 16) {
> +		if (xenfb->c.ds->depth == 8) {
> +		    BLT(uint16_t, uint8_t,   5, 6, 5,   3, 3, 2);
> +		} else if (xenfb->c.ds->depth == 32) {
> +		    BLT(uint16_t, uint32_t,  5, 6, 5,   8, 8, 8);
> +		}
> +	    } else if (xenfb->depth == 24 || xenfb->depth == 32) {
> +		if (xenfb->c.ds->depth == 8) {
> +		    BLT(uint32_t, uint8_t,   8, 8, 8,   3, 3, 2);
> +		} else if (xenfb->c.ds->depth == 16) {
> +		    BLT(uint32_t, uint16_t,  8, 8, 8,   5, 6, 5);
> +		} else if (xenfb->c.ds->depth == 32) {
> +		    BLT(uint32_t, uint32_t,  8, 8, 8,   8, 8, 8);
> +		}
> +	    }
> +	}
> +    }
> +    dpy_update(xenfb->c.ds, x, y, w, h);
> +}
> +
> +#ifdef XENFB_TYPE_REFRESH_PERIOD
> +static int xenfb_queue_full(struct xen_fb *xenfb)
> +{
> +    struct xenfb_page *page = xenfb->c.page;
> +    uint32_t cons, prod;
> +
> +    prod = page->in_prod;
> +    cons = page->in_cons;
> +    return prod - cons == XENFB_IN_RING_LEN;
> +}
> +
> +static void xenfb_send_event(struct xen_fb *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 xen_fb *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 xen_fb *xenfb = opaque;
> +    int i;
> +
> +    if (xenfb->feature_update) {
> +#ifdef XENFB_TYPE_REFRESH_PERIOD
> +	int period;
> +
> +	if (xenfb_queue_full(xenfb))
> +	    return;
> +
> +        /*
> +         * TODO: xen's qemu-dm seems to have some patches to
> +         *       make the qemu display code avoid unneeded
> +         *       work.
> +         *        - Port them over.
> +         *        - Put ds->idle back into use then.
> +         *        - Same goes for ds->shared_buf btw.
> +         */
> +	if (0 /* xenfb->c.ds->idle */)
> +	    period =  XENFB_NO_REFRESH;
> +	else {
> +	    period = xenfb->c.ds->gui_timer_interval;
> +	    if (!period)
> +		period = 30; // GUI_REFRESH_INTERVAL (see vl.c)
> +	}
> +
> +	if (xenfb->refresh_period != period) {
> +	    dprintf("%s: %d\n", __FUNCTION__, period);
> +	    xenfb_send_refresh_period(xenfb, period);
> +	    xenfb->refresh_period = 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->width != xenfb->c.ds->width || xenfb->height != xenfb->c.ds->height) {
> +	xen_be_printf(&xenfb->c.xendev, 1, "update: resizing\n");
> +	dpy_resize(xenfb->c.ds, xenfb->width, xenfb->height);
> +	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;
> +}
> +
> +static void xenfb_handle_events(struct xen_fb *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;
> +}
> +
> +/* QEMU display state changed, so refresh the framebuffer copy */
> +static void xenfb_invalidate(void *opaque)
> +{
> +    struct xen_fb *xenfb = opaque;
> +    xenfb->up_fullscreen = 1;
> +}
> +
> +static int fb_init(struct xendev *xendev)
> +{
> +    struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev);
> +
> +    if (!fb->c.ds) {
> +        /* xen_set_display() below will set that and trigger us again */
> +	xen_be_printf(xendev, 1, "ds not set (yet)\n");
> +	return -1;
> +    }
> +
> +    fb->refresh_period = -1;
> +
> +#ifdef XENFB_TYPE_RESIZE
> +    xenstore_write_be_int(xendev, "feature-resize", 1);
> +#endif
> +    return 0;
> +}
> +
> +static int fb_connect(struct xendev *xendev)
> +{
> +    struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev);
> +    struct xenfb_page *fb_page;
> +    int videoram;
> +    int rc;
> +
> +    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);
> +
> +    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;
> +
> +    graphic_console_init(fb->c.ds,
> +			 xenfb_update,
> +			 xenfb_invalidate,
> +			 NULL,
> +			 NULL,
> +			 fb);
> +
> +    xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n",
> +		  fb->feature_update, videoram);
> +
> +    return 0;
> +}
> +
> +static void fb_disconnect(struct xendev *xendev)
> +{
> +    struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev);
> +
> +    /* FIXME: Hmm, un-init gfx display? can qemu handle that? */
> +    common_unbind(&fb->c);
> +}
> +
> +static void fb_frontend_changed(struct xendev *xendev, const char *node)
> +{
> +    struct xen_fb *fb = container_of(xendev, struct xen_fb, 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_set_state(xendev, XenbusStateConnected);
> +	fb->bug_trigger = 1; /* only once */
> +    }
> +}
> +
> +static void fb_event(struct xendev *xendev)
> +{
> +    struct xen_fb *xenfb = container_of(xendev, struct xen_fb, c.xendev);
> +
> +    xenfb_handle_events(xenfb);
> +    xen_be_send_notify(&xenfb->c.xendev);
> +}
> +
> +/* -------------------------------------------------------------------- */
> +
> +struct devops xen_kbdmouse_ops = {
> +    .size       = sizeof(struct xen_input),
> +    .init       = input_init,
> +    .connect    = input_connect,
> +    .disconnect = input_disconnect,
> +    .event      = input_event,
> +};
> +
> +struct devops xen_framebuffer_ops = {
> +    .size       = sizeof(struct xen_fb),
> +    .init       = fb_init,
> +    .connect    = fb_connect,
> +    .disconnect = fb_disconnect,
> +    .event      = fb_event,
> +    .frontend_changed = fb_frontend_changed,
> +};
> +
> +static void xen_set_display_type(int domid, char *type, DisplayState *ds)
> +{
> +    struct xendev *xendev;
> +    struct xen_common *c;
> +
> +    xendev = xen_be_find_xendev(type, domid, 0);
> +    if (!xendev)
> +	return;
> +    c = container_of(xendev, struct xen_common, xendev);
> +    c->ds = ds;
> +    xen_be_printf(xendev, 1, "ds is %p\n", ds);
> +    /* retry ->init() */
> +    xen_be_check_state(xendev);
> +}
> +
> +void xen_set_display(int domid, DisplayState *ds)
> +{
> +    xen_set_display_type(domid, "vkbd", ds);
> +    xen_set_display_type(domid, "vfb", ds);
> +}
> diff --git a/hw/xen-machine.c b/hw/xen-machine.c
> index da10982..c03cb53 100644
> --- a/hw/xen-machine.c
> +++ b/hw/xen-machine.c
> @@ -59,6 +59,11 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size,
>      xen_be_init();
>  
>      xen_be_register("console", &xen_console_ops);
> +    xen_be_register("vkbd", &xen_kbdmouse_ops);
> +    xen_be_register("vfb", &xen_framebuffer_ops);
> +
> +    /* setup framebuffer */
> +    xen_set_display(xen_domid, ds);
>  }
>  
>  QEMUMachine xenpv_machine = {
>   

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 5/7] xen: add block device backend driver.
  2008-07-28 13:17   ` Gerd Hoffmann
  (?)
@ 2008-07-28 14:25   ` Anthony Liguori
  -1 siblings, 0 replies; 65+ messages in thread
From: Anthony Liguori @ 2008-07-28 14:25 UTC (permalink / raw)
  To: qemu-devel; +Cc: xen-devel, Gerd Hoffmann

Gerd Hoffmann wrote:
> 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    |  681 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/xen-machine.c |    1 +
>  sysemu.h         |    2 +-
>  vl.c             |    4 +
>  7 files changed, 793 insertions(+), 2 deletions(-)
>  create mode 100644 hw/xen-blkif.h
>  create mode 100644 hw/xen-disk.c
>
> diff --git a/Makefile.target b/Makefile.target
> index 66d41ee..2d599d2 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -517,7 +517,7 @@ endif
>  
>  # xen backend driver support
>  XEN_OBJS := xen-machine.o xen-backend.o
> -XEN_OBJS += xen-console.o xen-framebuffer.o
> +XEN_OBJS += xen-console.o xen-framebuffer.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 3facf90..941b0a6 100644
> --- a/hw/xen-backend.h
> +++ b/hw/xen-backend.h
> @@ -10,6 +10,7 @@
>  
>  #include "hw.h"
>  #include "xen.h"
> +#include "sysemu.h"
>  
>  /*
>   * tweaks needed to build with different xen versions
> @@ -118,6 +119,7 @@ void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...)
>  struct devops xen_console_ops;      /* xen_console.c     */
>  struct devops xen_kbdmouse_ops;     /* xen_framebuffer.c */
>  struct devops xen_framebuffer_ops;  /* xen_framebuffer.c */
> +struct devops xen_blkdev_ops;       /* xen_disk.c        */
>  
>  void xen_set_display(int domid, DisplayState *ds);
>  
> 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..6947363
> --- /dev/null
> +++ b/hw/xen-disk.c
> @@ -0,0 +1,681 @@
> +/*
> + *  xen paravirt block device backend
> + *
> + *  FIXME: the code is designed to handle multiple outstanding
> + *  requests (using aio or using threads), which isn't used right
> + *  now due to limitations of the qemu block driver interface.
> + *
> + *
> + *  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 <time.h>
> +#include <fcntl.h>
> +#include <errno.h>
> +#include <pthread.h>
>   

This doesn't seem to be needed?

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 6/7] xen: add net backend driver.
  2008-07-28 13:17   ` Gerd Hoffmann
  (?)
@ 2008-07-28 14:27   ` Anthony Liguori
  2008-07-28 15:45     ` Gerd Hoffmann
  -1 siblings, 1 reply; 65+ messages in thread
From: Anthony Liguori @ 2008-07-28 14:27 UTC (permalink / raw)
  To: qemu-devel; +Cc: xen-devel, Gerd Hoffmann

Gerd Hoffmann wrote:
> 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.c |    1 +
>  hw/xen-nic.c     |  448 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 451 insertions(+), 1 deletions(-)
>  create mode 100644 hw/xen-nic.c
>
> diff --git a/Makefile.target b/Makefile.target
> index 2d599d2..281d7fa 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -517,7 +517,7 @@ endif
>  
>  # xen backend driver support
>  XEN_OBJS := xen-machine.o xen-backend.o
> -XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o
> +XEN_OBJS += xen-console.o xen-framebuffer.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 941b0a6..2db2c3c 100644
> --- a/hw/xen-backend.h
> +++ b/hw/xen-backend.h
> @@ -120,6 +120,7 @@ struct devops xen_console_ops;      /* xen_console.c     */
>  struct devops xen_kbdmouse_ops;     /* xen_framebuffer.c */
>  struct devops xen_framebuffer_ops;  /* xen_framebuffer.c */
>  struct devops xen_blkdev_ops;       /* xen_disk.c        */
> +struct devops xen_netdev_ops;       /* xen_nic.c         */
>  
>  void xen_set_display(int domid, DisplayState *ds);
>  
> diff --git a/hw/xen-machine.c b/hw/xen-machine.c
> index 1b647a2..3fa4079 100644
> --- a/hw/xen-machine.c
> +++ b/hw/xen-machine.c
> @@ -62,6 +62,7 @@ static void xenpv_init(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_set_display(xen_domid, ds);
> diff --git a/hw/xen-nic.c b/hw/xen-nic.c
> new file mode 100644
> index 0000000..56d1474
> --- /dev/null
> +++ b/hw/xen-nic.c
> @@ -0,0 +1,448 @@
> +/*
> + *  xen paravirt network card backend
> + *
> + *  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 netdev {
> +    struct xendev         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;
> +};
> +
> +/* ------------------------------------------------------------- */
> +
> +#define PROTO_TCP  6
> +#define PROTO_UDP 17
> +
> +static uint32_t checksum_add(int len, uint8_t *buf)
> +{
> +    uint32_t sum = 0;
> +    int i;
> +
> +    for (i = 0; i < len; i++) {
> +	if (i & 1)
> +	    sum += (uint32_t)buf[i];
> +	else
> +	    sum += (uint32_t)buf[i] << 8;
> +    }
> +    return sum;
> +}
> +
> +static uint16_t checksum_finish(uint32_t sum)
> +{
> +    while (sum>>16)
> +	sum = (sum & 0xFFFF)+(sum >> 16);
> +    return ~sum;
> +}
> +
> +static uint16_t checksum_tcpudp(uint16_t length, uint16_t proto,
> +				uint8_t *addrs, uint8_t *buf)
> +{
> +    uint32_t sum = 0;
> +
> +    sum += checksum_add(length, buf);         // payload
> +    sum += checksum_add(8, addrs);            // src + dst address
> +    sum += proto + length;                    // protocol & length
> +    return checksum_finish(sum);
> +}
> +
> +static void checksum_calculate(uint8_t *data, int length)
> +{
> +    int hlen, plen, proto, csum_offset;
> +    uint16_t csum;
> +
> +    if ((data[14] & 0xf0) != 0x40)
> +	return; /* not IPv4 */
> +    hlen  = (data[14] & 0x0f) * 4;
> +    plen  = (data[16] << 8 | data[17]) - hlen;
> +    proto = data[23];
> +
> +    switch (proto) {
> +    case PROTO_TCP:
> +	csum_offset = 16;
> +	break;
> +    case PROTO_UDP:
> +	csum_offset = 6;
> +	break;
> +    default:
> +	return;
> +    }
> +
> +    if (plen < csum_offset+2)
> +	return;
> +
> +    data[14+hlen+csum_offset]   = 0;
> +    data[14+hlen+csum_offset+1] = 0;
> +    csum = checksum_tcpudp(plen, proto, data+14+12, data+14+hlen);
> +    data[14+hlen+csum_offset]   = csum >> 8;
> +    data[14+hlen+csum_offset+1] = csum & 0xff;
> +}
>
>   

The same thing is done in e1000, I believe.  We'll also probably want to 
be able to support this for virtio so this would be pretty good to make 
common code.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
  2008-07-28 14:22   ` [Qemu-devel] " Anthony Liguori
@ 2008-07-28 14:41       ` Andreas Färber
  2008-07-30  9:20       ` Gerd Hoffmann
  1 sibling, 0 replies; 65+ messages in thread
From: Andreas Färber @ 2008-07-28 14:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: xen-devel, Gerd Hoffmann


Am 28.07.2008 um 16:22 schrieb Anthony Liguori:

> Gerd Hoffmann wrote:
>> 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 unstable (aka son-to-be 3.3).
>>
>> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
>> ---
>> Makefile.target      |    2 +-
>> hw/xen-backend.h     |    4 +
>> hw/xen-framebuffer.c |  925 ++++++++++++++++++++++++++++++++++++++++ 
>> ++++++++++
>> hw/xen-machine.c     |    5 +
>> 4 files changed, 935 insertions(+), 1 deletions(-)
>> create mode 100644 hw/xen-framebuffer.c
>>
>> diff --git a/Makefile.target b/Makefile.target
>> index 05619fa..66d41ee 100644
>> --- a/Makefile.target
>> +++ b/Makefile.target
>> @@ -517,7 +517,7 @@ endif
>>  # xen backend driver support
>> XEN_OBJS := xen-machine.o xen-backend.o
>> -XEN_OBJS += xen-console.o
>> +XEN_OBJS += xen-console.o xen-framebuffer.o
>> ifeq ($(CONFIG_XEN), yes)
>>   OBJS += $(XEN_OBJS)
>>   LIBS += $(XEN_LIBS)
>> diff --git a/hw/xen-backend.h b/hw/xen-backend.h
>> index 55ffd31..3facf90 100644
>> --- a/hw/xen-backend.h
>> +++ b/hw/xen-backend.h
>> @@ -116,4 +116,8 @@ void xen_be_printf(struct xendev *xendev, int  
>> msg_level, const char *fmt, ...)
>>  /* actual backend drivers */
>> struct devops xen_console_ops;      /* xen_console.c     */
>> +struct devops xen_kbdmouse_ops;     /* xen_framebuffer.c */
>> +struct devops xen_framebuffer_ops;  /* xen_framebuffer.c */
>> +
>> +void xen_set_display(int domid, DisplayState *ds);
>> diff --git a/hw/xen-framebuffer.c b/hw/xen-framebuffer.c
>> new file mode 100644
>> index 0000000..716eafd
>> --- /dev/null
>> +++ b/hw/xen-framebuffer.c
>> @@ -0,0 +1,925 @@
>> +/*
>> + *  xen paravirt framebuffer backend
>>
>>
>
> No copyright?
>
>> + *  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 xen_common {
>> +    struct xendev     xendev;  /* must be first */
>> +    void              *page;
>> +    DisplayState      *ds;
>> +};
>> +
>> +struct xen_input {
>> +    struct xen_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 xen_fb {
>> +    struct xen_common c;
>> +    size_t            fb_len;
>> +    int               row_stride;
>> +    int               depth;
>> +    int               width;
>> +    int               height;
>> +    int               offset;
>> +    void              *pixels;
>> +    int               feature_update;
>> +    int               refresh_period;
>> +    int               bug_trigger;
>> +
>> +    struct {
>> +	int x,y,w,h;
>> +    } up_rects[UP_QUEUE];
>> +    int               up_count;
>> +    int               up_fullscreen;
>> +};
>> +
>> +/*  
>> -------------------------------------------------------------------- */
>> +
>> +static int common_bind(struct xen_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;
>> +
>> +    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);
>> +
>> +    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;
>> +    return 0;
>> +}
>> +
>> +static void common_unbind(struct xen_common *c)
>> +{
>> +    if (c->page) {
>> +	munmap(c->page, XC_PAGE_SIZE);
>> +	c->page = NULL;
>> +    }
>> +    xen_be_unbind_evtchn(&c->xendev);
>> +}
>> +
>> +/*  
>> -------------------------------------------------------------------- */
>> +
>> +/*
>> + * 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
>> +
>> +};
>> +
>> +static unsigned char scancode2linux[512];
>> +
>> +/* Send an event to the keyboard frontend driver */
>> +static int xenfb_kbd_event(struct xen_input *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;
>> +
>> +    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 xen_input *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 xen_input *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 xen_input *xenfb,
>> +			       int abs_x, int abs_y, int rel_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.rel_z = rel_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 xen_input *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 xen_input *xenfb = opaque;
>> +    int i;
>> +
>> +    if (xenfb->abs_pointer_wanted)
>> +	xenfb_send_position(xenfb,
>> +			    dx * (xenfb->c.ds->width - 1) / 0x7fff,
>> +			    dy * (xenfb->c.ds->height - 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 xendev *xendev)
>> +{
>> +    struct xen_input *in = container_of(xendev, struct xen_input,  
>> c.xendev);
>> +    static int first = 1;
>> +    int i;
>> +
>> +    if (first) {
>> +	/* Prepare scancode mapping table */
>> +	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];
>> +	}
>> +	first = 0;
>> +    }
>> +
>> +    if (!in->c.ds) {
>> +        /* xen_set_display() below will set that and trigger us  
>> again */
>> +        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 xendev *xendev)
>> +{
>> +    struct xen_input *in = container_of(xendev, struct xen_input,  
>> 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 xendev *xendev)
>> +{
>> +    struct xen_input *in = container_of(xendev, struct xen_input,  
>> 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 xendev *xendev)
>> +{
>> +    struct xen_input *xenfb = container_of(xendev, struct  
>> xen_input, 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 xen_fb *xenfb)
>> +{
>> +    struct xenfb_page *page = xenfb->c.page;
>> +    char *protocol = xenfb->c.xendev.protocol;
>> +    int n_fbmfns;
>> +    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
>> +    }
>> +
>> +    n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
>> +    n_fbdirs = n_fbmfns * mode / 8;
>> +    n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
>> +
>> +    pgmfns = malloc(sizeof(unsigned long) * n_fbdirs);
>> +    fbmfns = malloc(sizeof(unsigned long) * n_fbmfns);
>> +    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, n_fbmfns, 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, n_fbmfns);
>> +    if (xenfb->pixels == NULL)
>> +	goto out;
>> +
>> +    ret = 0; /* all is fine */
>> +
>> +out:
>> +    if (pgmfns)
>> +	free(pgmfns);
>> +    if (fbmfns)
>> +	free(fbmfns);
>> +    return ret;
>> +}
>> +
>> +static int xenfb_configure_fb(struct xen_fb *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;
>> +    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 *)(xenfb->c.ds->data				\
>> +			       + (line * xenfb->c.ds->linesize)		\
>> +			       + (x * xenfb->c.ds->depth / 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 + xenfb->c.ds->depth /  
>> 8); \
>> +	}								\
>> +    }
>>
>
> There are similar conversion macros in the VGA code.  I don't know  
> how practical it would be to reuse them but it's at least worth  
> looking at.

We created a file hw/pixel_ops.h to share code between vga.c and  
tcx.c, probably it can be extended to serve Xen as well.

Andreas

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 4/7] xen: add framebuffer backend driver
@ 2008-07-28 14:41       ` Andreas Färber
  0 siblings, 0 replies; 65+ messages in thread
From: Andreas Färber @ 2008-07-28 14:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: xen-devel, Gerd Hoffmann


Am 28.07.2008 um 16:22 schrieb Anthony Liguori:

> Gerd Hoffmann wrote:
>> 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 unstable (aka son-to-be 3.3).
>>
>> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
>> ---
>> Makefile.target      |    2 +-
>> hw/xen-backend.h     |    4 +
>> hw/xen-framebuffer.c |  925 ++++++++++++++++++++++++++++++++++++++++ 
>> ++++++++++
>> hw/xen-machine.c     |    5 +
>> 4 files changed, 935 insertions(+), 1 deletions(-)
>> create mode 100644 hw/xen-framebuffer.c
>>
>> diff --git a/Makefile.target b/Makefile.target
>> index 05619fa..66d41ee 100644
>> --- a/Makefile.target
>> +++ b/Makefile.target
>> @@ -517,7 +517,7 @@ endif
>>  # xen backend driver support
>> XEN_OBJS := xen-machine.o xen-backend.o
>> -XEN_OBJS += xen-console.o
>> +XEN_OBJS += xen-console.o xen-framebuffer.o
>> ifeq ($(CONFIG_XEN), yes)
>>   OBJS += $(XEN_OBJS)
>>   LIBS += $(XEN_LIBS)
>> diff --git a/hw/xen-backend.h b/hw/xen-backend.h
>> index 55ffd31..3facf90 100644
>> --- a/hw/xen-backend.h
>> +++ b/hw/xen-backend.h
>> @@ -116,4 +116,8 @@ void xen_be_printf(struct xendev *xendev, int  
>> msg_level, const char *fmt, ...)
>>  /* actual backend drivers */
>> struct devops xen_console_ops;      /* xen_console.c     */
>> +struct devops xen_kbdmouse_ops;     /* xen_framebuffer.c */
>> +struct devops xen_framebuffer_ops;  /* xen_framebuffer.c */
>> +
>> +void xen_set_display(int domid, DisplayState *ds);
>> diff --git a/hw/xen-framebuffer.c b/hw/xen-framebuffer.c
>> new file mode 100644
>> index 0000000..716eafd
>> --- /dev/null
>> +++ b/hw/xen-framebuffer.c
>> @@ -0,0 +1,925 @@
>> +/*
>> + *  xen paravirt framebuffer backend
>>
>>
>
> No copyright?
>
>> + *  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 xen_common {
>> +    struct xendev     xendev;  /* must be first */
>> +    void              *page;
>> +    DisplayState      *ds;
>> +};
>> +
>> +struct xen_input {
>> +    struct xen_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 xen_fb {
>> +    struct xen_common c;
>> +    size_t            fb_len;
>> +    int               row_stride;
>> +    int               depth;
>> +    int               width;
>> +    int               height;
>> +    int               offset;
>> +    void              *pixels;
>> +    int               feature_update;
>> +    int               refresh_period;
>> +    int               bug_trigger;
>> +
>> +    struct {
>> +	int x,y,w,h;
>> +    } up_rects[UP_QUEUE];
>> +    int               up_count;
>> +    int               up_fullscreen;
>> +};
>> +
>> +/*  
>> -------------------------------------------------------------------- */
>> +
>> +static int common_bind(struct xen_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;
>> +
>> +    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);
>> +
>> +    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;
>> +    return 0;
>> +}
>> +
>> +static void common_unbind(struct xen_common *c)
>> +{
>> +    if (c->page) {
>> +	munmap(c->page, XC_PAGE_SIZE);
>> +	c->page = NULL;
>> +    }
>> +    xen_be_unbind_evtchn(&c->xendev);
>> +}
>> +
>> +/*  
>> -------------------------------------------------------------------- */
>> +
>> +/*
>> + * 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
>> +
>> +};
>> +
>> +static unsigned char scancode2linux[512];
>> +
>> +/* Send an event to the keyboard frontend driver */
>> +static int xenfb_kbd_event(struct xen_input *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;
>> +
>> +    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 xen_input *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 xen_input *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 xen_input *xenfb,
>> +			       int abs_x, int abs_y, int rel_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.rel_z = rel_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 xen_input *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 xen_input *xenfb = opaque;
>> +    int i;
>> +
>> +    if (xenfb->abs_pointer_wanted)
>> +	xenfb_send_position(xenfb,
>> +			    dx * (xenfb->c.ds->width - 1) / 0x7fff,
>> +			    dy * (xenfb->c.ds->height - 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 xendev *xendev)
>> +{
>> +    struct xen_input *in = container_of(xendev, struct xen_input,  
>> c.xendev);
>> +    static int first = 1;
>> +    int i;
>> +
>> +    if (first) {
>> +	/* Prepare scancode mapping table */
>> +	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];
>> +	}
>> +	first = 0;
>> +    }
>> +
>> +    if (!in->c.ds) {
>> +        /* xen_set_display() below will set that and trigger us  
>> again */
>> +        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 xendev *xendev)
>> +{
>> +    struct xen_input *in = container_of(xendev, struct xen_input,  
>> 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 xendev *xendev)
>> +{
>> +    struct xen_input *in = container_of(xendev, struct xen_input,  
>> 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 xendev *xendev)
>> +{
>> +    struct xen_input *xenfb = container_of(xendev, struct  
>> xen_input, 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 xen_fb *xenfb)
>> +{
>> +    struct xenfb_page *page = xenfb->c.page;
>> +    char *protocol = xenfb->c.xendev.protocol;
>> +    int n_fbmfns;
>> +    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
>> +    }
>> +
>> +    n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
>> +    n_fbdirs = n_fbmfns * mode / 8;
>> +    n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
>> +
>> +    pgmfns = malloc(sizeof(unsigned long) * n_fbdirs);
>> +    fbmfns = malloc(sizeof(unsigned long) * n_fbmfns);
>> +    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, n_fbmfns, 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, n_fbmfns);
>> +    if (xenfb->pixels == NULL)
>> +	goto out;
>> +
>> +    ret = 0; /* all is fine */
>> +
>> +out:
>> +    if (pgmfns)
>> +	free(pgmfns);
>> +    if (fbmfns)
>> +	free(fbmfns);
>> +    return ret;
>> +}
>> +
>> +static int xenfb_configure_fb(struct xen_fb *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;
>> +    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 *)(xenfb->c.ds->data				\
>> +			       + (line * xenfb->c.ds->linesize)		\
>> +			       + (x * xenfb->c.ds->depth / 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 + xenfb->c.ds->depth /  
>> 8); \
>> +	}								\
>> +    }
>>
>
> There are similar conversion macros in the VGA code.  I don't know  
> how practical it would be to reuse them but it's at least worth  
> looking at.

We created a file hw/pixel_ops.h to share code between vga.c and  
tcx.c, probably it can be extended to serve Xen as well.

Andreas

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
  2008-07-28 14:04   ` [Qemu-devel] " Anthony Liguori
@ 2008-07-28 14:52     ` Gerd Hoffmann
  2008-07-29  8:10         ` Daniel P. Berrange
  2008-07-28 23:14       ` Samuel Thibault
  1 sibling, 1 reply; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 14:52 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: xen-devel, qemu-devel

>> +/* xen_machine.c */
>> +extern QEMUMachine xenpv_machine;
>> +extern QEMUMachine xenfv_machine;
> 
> Why does xenfv need its own machine type?

This is how xen's qemu-dm handles it at the moment and I've decided to
do it the same way for simplicity.  I think qemu could also figure it
using a hypercall.

>> -    /* XXX: this should not be: some embedded targets just have flash */
>> +    /* need a disk for this machine to boot ? */
>> +    /* XXX: add embedded targets which just have flash */
>> +    nodisk_ok = 0;
>> +    if (0 == strcmp(machine->name, "xenpv"))
>> +    nodisk_ok = 1;
>>      if (!linux_boot && net_boot == 0 &&
>> -        nb_drives_opt == 0)
>> +        !nodisk_ok && nb_drives_opt == 0)
>>          help(1);
> 
> This patch is pretty clean with the exception of this bit.  I think a
> cleaner way to do this would be to let the machine types specify whether
> a disk is needed or not.

Agreed, I'll make it a machine_type field.

thanks,
  Gerd

-- 
http://kraxel.fedorapeople.org/xenner/

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 3/7] xen: add console backend driver.
  2008-07-28 14:17   ` [Qemu-devel] " Anthony Liguori
@ 2008-07-28 15:43     ` Gerd Hoffmann
  2008-07-28 19:04       ` Anthony Liguori
  0 siblings, 1 reply; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 15:43 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: xen-devel, qemu-devel

>> +
>> +struct devops xen_console_ops = {
>>   
> 
> I missed it in the previous patchset, but it would be nice to QEMU-ify
> these things.  For instance, 'struct xen_console' => 'XenConsole',
> 'struct devopts' => 'XenDevOpts', etc.

Hmm, I will if I have to.

But I don't like that style very much and a quick grep through the qemu
header files shows that the CapsStyle isn't used consequently
everythere.  I see a happy style mix.  The core structures tend to be
CapsStyle though ...

cheers,
  Gerd

-- 
http://kraxel.fedorapeople.org/xenner/

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 6/7] xen: add net backend driver.
  2008-07-28 14:27   ` [Qemu-devel] " Anthony Liguori
@ 2008-07-28 15:45     ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 15:45 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: xen-devel, qemu-devel

Anthony Liguori wrote:
> Gerd Hoffmann wrote:
>> +static void checksum_calculate(uint8_t *data, int length)
>> +{
> 
> The same thing is done in e1000, I believe.  We'll also probably want to
> be able to support this for virtio so this would be pretty good to make
> common code.

Good point, I'll split it into a separate patch and source file.

cheers,
  Gerd

-- 
http://kraxel.fedorapeople.org/xenner/

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 2/7] xen: backend driver core
  2008-07-28 14:13   ` [Qemu-devel] " Anthony Liguori
@ 2008-07-28 15:51     ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-28 15:51 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: xen-devel, qemu-devel

Anthony Liguori wrote:
>> --- /dev/null
>> +++ b/hw/list.h
>>   
> 
> There's already a list implementation in audio/sys-queue.h

Well hidden ;)
Thanks for the pointer.

> I think it would be good to provide some more commenting in this file. 
> Just a high level description of XenStore and how all of this stuff fits
> together would be nice.

Probably a good idea, yes.

thanks,
  Gerd

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 3/7] xen: add console backend driver.
  2008-07-28 15:43     ` Gerd Hoffmann
@ 2008-07-28 19:04       ` Anthony Liguori
  0 siblings, 0 replies; 65+ messages in thread
From: Anthony Liguori @ 2008-07-28 19:04 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: xen-devel, qemu-devel

Gerd Hoffmann wrote:
>>> +
>>> +struct devops xen_console_ops = {
>>>   
>>>       
>> I missed it in the previous patchset, but it would be nice to QEMU-ify
>> these things.  For instance, 'struct xen_console' => 'XenConsole',
>> 'struct devopts' => 'XenDevOpts', etc.
>>     
>
> Hmm, I will if I have to.
>
> But I don't like that style very much and a quick grep through the qemu
> header files shows that the CapsStyle isn't used consequently
> everythere.  I see a happy style mix.  The core structures tend to be
> CapsStyle though ...
>   

I much prefer the Linux style myself, but I prefer consistency over any 
particular style.  I think it would be good to modify the code to be 
consistent with the rest of the core QEMU structure.

Regards,

Anthony Liguori

> cheers,
>   Gerd
>
>   

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
  2008-07-28 14:04   ` [Qemu-devel] " Anthony Liguori
@ 2008-07-28 23:14       ` Samuel Thibault
  2008-07-28 23:14       ` Samuel Thibault
  1 sibling, 0 replies; 65+ messages in thread
From: Samuel Thibault @ 2008-07-28 23:14 UTC (permalink / raw)
  To: qemu-devel; +Cc: xen-devel, Gerd Hoffmann

Anthony Liguori, le Mon 28 Jul 2008 09:04:54 -0500, a écrit :
> >+/* xen_machine.c */
> >+extern QEMUMachine xenpv_machine;
> >+extern QEMUMachine xenfv_machine;
> 
> Why does xenfv need its own machine type?

IIRC that's at least because it adds its own Xen platform -specific PCI card.

Samuel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
@ 2008-07-28 23:14       ` Samuel Thibault
  0 siblings, 0 replies; 65+ messages in thread
From: Samuel Thibault @ 2008-07-28 23:14 UTC (permalink / raw)
  To: qemu-devel; +Cc: xen-devel, Gerd Hoffmann

Anthony Liguori, le Mon 28 Jul 2008 09:04:54 -0500, a écrit :
> >+/* xen_machine.c */
> >+extern QEMUMachine xenpv_machine;
> >+extern QEMUMachine xenfv_machine;
> 
> Why does xenfv need its own machine type?

IIRC that's at least because it adds its own Xen platform -specific PCI card.

Samuel

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
  2008-07-28 23:14       ` Samuel Thibault
@ 2008-07-29  7:38         ` Gerd Hoffmann
  -1 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-29  7:38 UTC (permalink / raw)
  To: Samuel Thibault, qemu-devel, xen-devel, Gerd Hoffmann

Samuel Thibault wrote:
> Anthony Liguori, le Mon 28 Jul 2008 09:04:54 -0500, a écrit :
>>> +/* xen_machine.c */
>>> +extern QEMUMachine xenpv_machine;
>>> +extern QEMUMachine xenfv_machine;
>> Why does xenfv need its own machine type?
> 
> IIRC that's at least because it adds its own Xen platform -specific PCI card.

That is only one tiny bit of the differences between paravirtual and
fully virtualized machines, there is plenty more.  The question is
whenever qemu should try figure itself whenever the virtual machine it
serves is fully virtualized or whenever we'll explicitly tell it qemu.

I think it is better to explicitly tell qemu whenever we want create a
paravirtual or a full virtualized machine.  First, because this is how
it is handled right now in xen (via -M [ xenpv | xenfv ] ), and second,
it leaves the door open to have qemu also create the domain.

cheers,
  Gerd

-- 
http://kraxel.fedorapeople.org/xenner/

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
@ 2008-07-29  7:38         ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-29  7:38 UTC (permalink / raw)
  To: Samuel Thibault, qemu-devel, xen-devel, Gerd Hoffmann

Samuel Thibault wrote:
> Anthony Liguori, le Mon 28 Jul 2008 09:04:54 -0500, a écrit :
>>> +/* xen_machine.c */
>>> +extern QEMUMachine xenpv_machine;
>>> +extern QEMUMachine xenfv_machine;
>> Why does xenfv need its own machine type?
> 
> IIRC that's at least because it adds its own Xen platform -specific PCI card.

That is only one tiny bit of the differences between paravirtual and
fully virtualized machines, there is plenty more.  The question is
whenever qemu should try figure itself whenever the virtual machine it
serves is fully virtualized or whenever we'll explicitly tell it qemu.

I think it is better to explicitly tell qemu whenever we want create a
paravirtual or a full virtualized machine.  First, because this is how
it is handled right now in xen (via -M [ xenpv | xenfv ] ), and second,
it leaves the door open to have qemu also create the domain.

cheers,
  Gerd

-- 
http://kraxel.fedorapeople.org/xenner/

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
  2008-07-28 14:52     ` Gerd Hoffmann
@ 2008-07-29  8:10         ` Daniel P. Berrange
  0 siblings, 0 replies; 65+ messages in thread
From: Daniel P. Berrange @ 2008-07-29  8:10 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: xen-devel, qemu-devel

On Mon, Jul 28, 2008 at 04:52:28PM +0200, Gerd Hoffmann wrote:
> >> +/* xen_machine.c */
> >> +extern QEMUMachine xenpv_machine;
> >> +extern QEMUMachine xenfv_machine;
> > 
> > Why does xenfv need its own machine type?
> 
> This is how xen's qemu-dm handles it at the moment and I've decided to
> do it the same way for simplicity.  I think qemu could also figure it
> using a hypercall.

This is something I originally added to QEMU in Xen's tree. The basic
idea is that the 'xenpv' macjine is a machine which /only/ provides the
paravirtualized Xen backends drivers. The 'xenfv' machine type is just
the same as the 'pc' machine type, but with the /addition/ of the Xen
paravirtualized backends. Perhaps the latter could be thought of as
more of a 'xenpc' (cf 'pc' and 'isapc') becasue its a variation on the
regular PC machine type. Annoying that all the acronyms I come up with
only differ by one letter :-)

Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
@ 2008-07-29  8:10         ` Daniel P. Berrange
  0 siblings, 0 replies; 65+ messages in thread
From: Daniel P. Berrange @ 2008-07-29  8:10 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: xen-devel, qemu-devel, Anthony Liguori

On Mon, Jul 28, 2008 at 04:52:28PM +0200, Gerd Hoffmann wrote:
> >> +/* xen_machine.c */
> >> +extern QEMUMachine xenpv_machine;
> >> +extern QEMUMachine xenfv_machine;
> > 
> > Why does xenfv need its own machine type?
> 
> This is how xen's qemu-dm handles it at the moment and I've decided to
> do it the same way for simplicity.  I think qemu could also figure it
> using a hypercall.

This is something I originally added to QEMU in Xen's tree. The basic
idea is that the 'xenpv' macjine is a machine which /only/ provides the
paravirtualized Xen backends drivers. The 'xenfv' machine type is just
the same as the 'pc' machine type, but with the /addition/ of the Xen
paravirtualized backends. Perhaps the latter could be thought of as
more of a 'xenpc' (cf 'pc' and 'isapc') becasue its a variation on the
regular PC machine type. Annoying that all the acronyms I come up with
only differ by one letter :-)

Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
  2008-07-29  7:38         ` Gerd Hoffmann
@ 2008-07-29  8:12           ` Daniel P. Berrange
  -1 siblings, 0 replies; 65+ messages in thread
From: Daniel P. Berrange @ 2008-07-29  8:12 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: xen-devel, qemu-devel, Samuel Thibault

On Tue, Jul 29, 2008 at 09:38:01AM +0200, Gerd Hoffmann wrote:
> Samuel Thibault wrote:
> > Anthony Liguori, le Mon 28 Jul 2008 09:04:54 -0500, a écrit :
> >>> +/* xen_machine.c */
> >>> +extern QEMUMachine xenpv_machine;
> >>> +extern QEMUMachine xenfv_machine;
> >> Why does xenfv need its own machine type?
> > 
> > IIRC that's at least because it adds its own Xen platform -specific PCI card.
> 
> That is only one tiny bit of the differences between paravirtual and
> fully virtualized machines, there is plenty more.  The question is
> whenever qemu should try figure itself whenever the virtual machine it
> serves is fully virtualized or whenever we'll explicitly tell it qemu.
> 
> I think it is better to explicitly tell qemu whenever we want create a
> paravirtual or a full virtualized machine.  First, because this is how
> it is handled right now in xen (via -M [ xenpv | xenfv ] ), and second,
> it leaves the door open to have qemu also create the domain.

The setup process for 'xenpv' vs 'xenfv' is really very different. As I
mentioned in the other mail, 'xenfv' is really much closer to 'pc'
machine type, then 'xenpv'.  Thus I think its better to keep them
explicitly separate - they call out to shared code where needed anyway,
so there's no serious code duplication problem there.

Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
@ 2008-07-29  8:12           ` Daniel P. Berrange
  0 siblings, 0 replies; 65+ messages in thread
From: Daniel P. Berrange @ 2008-07-29  8:12 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: xen-devel, qemu-devel, Samuel Thibault

On Tue, Jul 29, 2008 at 09:38:01AM +0200, Gerd Hoffmann wrote:
> Samuel Thibault wrote:
> > Anthony Liguori, le Mon 28 Jul 2008 09:04:54 -0500, a écrit :
> >>> +/* xen_machine.c */
> >>> +extern QEMUMachine xenpv_machine;
> >>> +extern QEMUMachine xenfv_machine;
> >> Why does xenfv need its own machine type?
> > 
> > IIRC that's at least because it adds its own Xen platform -specific PCI card.
> 
> That is only one tiny bit of the differences between paravirtual and
> fully virtualized machines, there is plenty more.  The question is
> whenever qemu should try figure itself whenever the virtual machine it
> serves is fully virtualized or whenever we'll explicitly tell it qemu.
> 
> I think it is better to explicitly tell qemu whenever we want create a
> paravirtual or a full virtualized machine.  First, because this is how
> it is handled right now in xen (via -M [ xenpv | xenfv ] ), and second,
> it leaves the door open to have qemu also create the domain.

The setup process for 'xenpv' vs 'xenfv' is really very different. As I
mentioned in the other mail, 'xenfv' is really much closer to 'pc'
machine type, then 'xenpv'.  Thus I think its better to keep them
explicitly separate - they call out to shared code where needed anyway,
so there's no serious code duplication problem there.

Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
  2008-07-29  8:12           ` Daniel P. Berrange
@ 2008-07-29  8:55             ` Gerd Hoffmann
  -1 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-29  8:55 UTC (permalink / raw)
  To: Daniel P. Berrange, qemu-devel; +Cc: xen-devel, Samuel Thibault

Daniel P. Berrange wrote:

> The setup process for 'xenpv' vs 'xenfv' is really very different. As I
> mentioned in the other mail, 'xenfv' is really much closer to 'pc'
> machine type, then 'xenpv'.

Yes.  I think we will keep the xenpv machine type for sure.  For full
virtualization it might be more useful to just use the "pc" machine type
(and maybe make xenfv an alias for compatibility).

>From qemu's point of view xen and kvm are not very different: no cpu
emulation needed, some chips such as apic are handled by someone else
too, some additional devices might be there (xen platform device, virtio
devices).  So it would make sense to handle them the same way,
especially once we have the qemuaccel stuff in place which will add an
abstraction layer for this.

cheers,
  Gerd

-- 
http://kraxel.fedorapeople.org/xenner/

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
@ 2008-07-29  8:55             ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-29  8:55 UTC (permalink / raw)
  To: Daniel P. Berrange, qemu-devel; +Cc: xen-devel, Samuel Thibault

Daniel P. Berrange wrote:

> The setup process for 'xenpv' vs 'xenfv' is really very different. As I
> mentioned in the other mail, 'xenfv' is really much closer to 'pc'
> machine type, then 'xenpv'.

Yes.  I think we will keep the xenpv machine type for sure.  For full
virtualization it might be more useful to just use the "pc" machine type
(and maybe make xenfv an alias for compatibility).

>From qemu's point of view xen and kvm are not very different: no cpu
emulation needed, some chips such as apic are handled by someone else
too, some additional devices might be there (xen platform device, virtio
devices).  So it would make sense to handle them the same way,
especially once we have the qemuaccel stuff in place which will add an
abstraction layer for this.

cheers,
  Gerd

-- 
http://kraxel.fedorapeople.org/xenner/

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
  2008-07-29  8:10         ` Daniel P. Berrange
@ 2008-07-29 13:32           ` Anthony Liguori
  -1 siblings, 0 replies; 65+ messages in thread
From: Anthony Liguori @ 2008-07-29 13:32 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: xen-devel, Gerd Hoffmann, qemu-devel

Daniel P. Berrange wrote:
> On Mon, Jul 28, 2008 at 04:52:28PM +0200, Gerd Hoffmann wrote:
>   
>>>> +/* xen_machine.c */
>>>> +extern QEMUMachine xenpv_machine;
>>>> +extern QEMUMachine xenfv_machine;
>>>>         
>>> Why does xenfv need its own machine type?
>>>       
>> This is how xen's qemu-dm handles it at the moment and I've decided to
>> do it the same way for simplicity.  I think qemu could also figure it
>> using a hypercall.
>>     
>
> This is something I originally added to QEMU in Xen's tree. The basic
> idea is that the 'xenpv' macjine is a machine which /only/ provides the
> paravirtualized Xen backends drivers. The 'xenfv' machine type is just
> the same as the 'pc' machine type, but with the /addition/ of the Xen
> paravirtualized backends. Perhaps the latter could be thought of as
> more of a 'xenpc' (cf 'pc' and 'isapc') becasue its a variation on the
> regular PC machine type. Annoying that all the acronyms I come up with
> only differ by one letter :-)
>   

Why wouldn't the Xen backends be added by appropriate -net or -drive 
options?  For instance, qemu -drive file=foo.img,if=xen -net nic,model=xen

Regards,

Anthony Liguori

> Daniel
>   

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
@ 2008-07-29 13:32           ` Anthony Liguori
  0 siblings, 0 replies; 65+ messages in thread
From: Anthony Liguori @ 2008-07-29 13:32 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: xen-devel, Gerd Hoffmann, qemu-devel

Daniel P. Berrange wrote:
> On Mon, Jul 28, 2008 at 04:52:28PM +0200, Gerd Hoffmann wrote:
>   
>>>> +/* xen_machine.c */
>>>> +extern QEMUMachine xenpv_machine;
>>>> +extern QEMUMachine xenfv_machine;
>>>>         
>>> Why does xenfv need its own machine type?
>>>       
>> This is how xen's qemu-dm handles it at the moment and I've decided to
>> do it the same way for simplicity.  I think qemu could also figure it
>> using a hypercall.
>>     
>
> This is something I originally added to QEMU in Xen's tree. The basic
> idea is that the 'xenpv' macjine is a machine which /only/ provides the
> paravirtualized Xen backends drivers. The 'xenfv' machine type is just
> the same as the 'pc' machine type, but with the /addition/ of the Xen
> paravirtualized backends. Perhaps the latter could be thought of as
> more of a 'xenpc' (cf 'pc' and 'isapc') becasue its a variation on the
> regular PC machine type. Annoying that all the acronyms I come up with
> only differ by one letter :-)
>   

Why wouldn't the Xen backends be added by appropriate -net or -drive 
options?  For instance, qemu -drive file=foo.img,if=xen -net nic,model=xen

Regards,

Anthony Liguori

> Daniel
>   

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
  2008-07-29 13:32           ` Anthony Liguori
@ 2008-07-29 14:24             ` Daniel P. Berrange
  -1 siblings, 0 replies; 65+ messages in thread
From: Daniel P. Berrange @ 2008-07-29 14:24 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: xen-devel, Gerd Hoffmann, qemu-devel

On Tue, Jul 29, 2008 at 08:32:11AM -0500, Anthony Liguori wrote:
> Daniel P. Berrange wrote:
> >On Mon, Jul 28, 2008 at 04:52:28PM +0200, Gerd Hoffmann wrote:
> >  
> >>>>+/* xen_machine.c */
> >>>>+extern QEMUMachine xenpv_machine;
> >>>>+extern QEMUMachine xenfv_machine;
> >>>>        
> >>>Why does xenfv need its own machine type?
> >>>      
> >>This is how xen's qemu-dm handles it at the moment and I've decided to
> >>do it the same way for simplicity.  I think qemu could also figure it
> >>using a hypercall.
> >>    
> >
> >This is something I originally added to QEMU in Xen's tree. The basic
> >idea is that the 'xenpv' macjine is a machine which /only/ provides the
> >paravirtualized Xen backends drivers. The 'xenfv' machine type is just
> >the same as the 'pc' machine type, but with the /addition/ of the Xen
> >paravirtualized backends. Perhaps the latter could be thought of as
> >more of a 'xenpc' (cf 'pc' and 'isapc') becasue its a variation on the
> >regular PC machine type. Annoying that all the acronyms I come up with
> >only differ by one letter :-)
> >  
> 
> Why wouldn't the Xen backends be added by appropriate -net or -drive 
> options?  For instance, qemu -drive file=foo.img,if=xen -net nic,model=xen

That would work for certain setup tasks, but not all. In Xen's branch
of QEMU, the machine init function has to set various hypervisor 
parameters which aren't directly associated with QEMU command line
args, and initialize things like the map-cache for > 4GB guest support.
In the Xennite work I had further stuff going on there too - more general
initialization of Xenstore parameters - eg stuffing the name & uuid from
QEMU's argv into right place in xenstore. 

Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
@ 2008-07-29 14:24             ` Daniel P. Berrange
  0 siblings, 0 replies; 65+ messages in thread
From: Daniel P. Berrange @ 2008-07-29 14:24 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: xen-devel, Gerd Hoffmann, qemu-devel

On Tue, Jul 29, 2008 at 08:32:11AM -0500, Anthony Liguori wrote:
> Daniel P. Berrange wrote:
> >On Mon, Jul 28, 2008 at 04:52:28PM +0200, Gerd Hoffmann wrote:
> >  
> >>>>+/* xen_machine.c */
> >>>>+extern QEMUMachine xenpv_machine;
> >>>>+extern QEMUMachine xenfv_machine;
> >>>>        
> >>>Why does xenfv need its own machine type?
> >>>      
> >>This is how xen's qemu-dm handles it at the moment and I've decided to
> >>do it the same way for simplicity.  I think qemu could also figure it
> >>using a hypercall.
> >>    
> >
> >This is something I originally added to QEMU in Xen's tree. The basic
> >idea is that the 'xenpv' macjine is a machine which /only/ provides the
> >paravirtualized Xen backends drivers. The 'xenfv' machine type is just
> >the same as the 'pc' machine type, but with the /addition/ of the Xen
> >paravirtualized backends. Perhaps the latter could be thought of as
> >more of a 'xenpc' (cf 'pc' and 'isapc') becasue its a variation on the
> >regular PC machine type. Annoying that all the acronyms I come up with
> >only differ by one letter :-)
> >  
> 
> Why wouldn't the Xen backends be added by appropriate -net or -drive 
> options?  For instance, qemu -drive file=foo.img,if=xen -net nic,model=xen

That would work for certain setup tasks, but not all. In Xen's branch
of QEMU, the machine init function has to set various hypervisor 
parameters which aren't directly associated with QEMU command line
args, and initialize things like the map-cache for > 4GB guest support.
In the Xennite work I had further stuff going on there too - more general
initialization of Xenstore parameters - eg stuffing the name & uuid from
QEMU's argv into right place in xenstore. 

Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
  2008-07-29 13:32           ` Anthony Liguori
@ 2008-07-29 14:32             ` Gerd Hoffmann
  -1 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-29 14:32 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: xen-devel, qemu-devel

Anthony Liguori wrote:
> Daniel P. Berrange wrote:
>> This is something I originally added to QEMU in Xen's tree. The basic
>> idea is that the 'xenpv' macjine is a machine which /only/ provides the
>> paravirtualized Xen backends drivers. The 'xenfv' machine type is just
>> the same as the 'pc' machine type, but with the /addition/ of the Xen
>> paravirtualized backends. Perhaps the latter could be thought of as
>> more of a 'xenpc' (cf 'pc' and 'isapc') becasue its a variation on the
>> regular PC machine type. Annoying that all the acronyms I come up with
>> only differ by one letter :-)
> 
> Why wouldn't the Xen backends be added by appropriate -net or -drive
> options?  For instance, qemu -drive file=foo.img,if=xen -net nic,model=xen

They can and I plan to support exactly that syntax ;)


Lets us step back a bit.  xenbus handles all the device discovery.
xenstore holds (among other things) all the information xenbus needs to
discover devices.

The xen frontend devices (guest side of the split drivers) have the
configuration information in this xenstore path:

  /local/domain/$domid/device/$type/$nr/

The xen backend devices (host side of the split drivers) have the
configuration information in this path:

  /local/domain/$domid/backend/$type/$domid/$nr/

where the first domid is where the backend runs in (which is Domain-0
unless you are using driver domains).  The second domid is the guest
domain.  The $type doesn't need to be identical, so you can have
different backend drivers serving the same frontend driver.  Which
actually is the case for block devices (blkback & blktap).


The frontend and backend drivers usually scan their xenstore directories
on initialization and install xenstore watches to see devices come and go.

What needs to be done to create a xenbus device is create those two
directories (one frontend, one backend) and populate the nodes therein
with the information required (some generic, some device specific).


All the backend drivers in my patch series are using xenstore/xenbus for
device discovery as usual.  The last patch (#7) actually winds up
command line support: you can specify xen nics and disks then with the
syntax listed above, and qemu will go and create the xenstore
directories for backend and frontend.  It could also be someone else
creating them (xend, xenner), that will work equally well.

cheers,
  Gerd

-- 
http://kraxel.fedorapeople.org/xenner/

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
@ 2008-07-29 14:32             ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-29 14:32 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: xen-devel, Daniel P. Berrange, qemu-devel

Anthony Liguori wrote:
> Daniel P. Berrange wrote:
>> This is something I originally added to QEMU in Xen's tree. The basic
>> idea is that the 'xenpv' macjine is a machine which /only/ provides the
>> paravirtualized Xen backends drivers. The 'xenfv' machine type is just
>> the same as the 'pc' machine type, but with the /addition/ of the Xen
>> paravirtualized backends. Perhaps the latter could be thought of as
>> more of a 'xenpc' (cf 'pc' and 'isapc') becasue its a variation on the
>> regular PC machine type. Annoying that all the acronyms I come up with
>> only differ by one letter :-)
> 
> Why wouldn't the Xen backends be added by appropriate -net or -drive
> options?  For instance, qemu -drive file=foo.img,if=xen -net nic,model=xen

They can and I plan to support exactly that syntax ;)


Lets us step back a bit.  xenbus handles all the device discovery.
xenstore holds (among other things) all the information xenbus needs to
discover devices.

The xen frontend devices (guest side of the split drivers) have the
configuration information in this xenstore path:

  /local/domain/$domid/device/$type/$nr/

The xen backend devices (host side of the split drivers) have the
configuration information in this path:

  /local/domain/$domid/backend/$type/$domid/$nr/

where the first domid is where the backend runs in (which is Domain-0
unless you are using driver domains).  The second domid is the guest
domain.  The $type doesn't need to be identical, so you can have
different backend drivers serving the same frontend driver.  Which
actually is the case for block devices (blkback & blktap).


The frontend and backend drivers usually scan their xenstore directories
on initialization and install xenstore watches to see devices come and go.

What needs to be done to create a xenbus device is create those two
directories (one frontend, one backend) and populate the nodes therein
with the information required (some generic, some device specific).


All the backend drivers in my patch series are using xenstore/xenbus for
device discovery as usual.  The last patch (#7) actually winds up
command line support: you can specify xen nics and disks then with the
syntax listed above, and qemu will go and create the xenstore
directories for backend and frontend.  It could also be someone else
creating them (xend, xenner), that will work equally well.

cheers,
  Gerd

-- 
http://kraxel.fedorapeople.org/xenner/

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
  2008-07-29 14:24             ` Daniel P. Berrange
@ 2008-07-29 19:11               ` Anthony Liguori
  -1 siblings, 0 replies; 65+ messages in thread
From: Anthony Liguori @ 2008-07-29 19:11 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: xen-devel, Gerd Hoffmann, qemu-devel

Daniel P. Berrange wrote:
> On Tue, Jul 29, 2008 at 08:32:11AM -0500, Anthony Liguori wrote:
>   
>> Why wouldn't the Xen backends be added by appropriate -net or -drive 
>> options?  For instance, qemu -drive file=foo.img,if=xen -net nic,model=xen
>>     
>
> That would work for certain setup tasks, but not all. In Xen's branch
> of QEMU, the machine init function has to set various hypervisor 
> parameters which aren't directly associated with QEMU command line
> args, and initialize things like the map-cache for > 4GB guest support.
>   

map-cache is one of those things I don't expect to ever get merged.

> In the Xennite work I had further stuff going on there too - more general
> initialization of Xenstore parameters - eg stuffing the name & uuid from
> QEMU's argv into right place in xenstore. 
>   

Ideally, I'd like to see Xen/KVM integration look like this:

1) Xen support is detected in configure (libxc et al) and conditionally 
enabled.
2) When running on bare metal, detect whether KVM acceleration is 
available, also detect if kqemu acceleration is available
3) When running under Xen, detect that Xen is available, and create a 
full virt domain
4) If a user specifies a type=xen device, it should Just Work provided 
you are in a Xen environment (erroring appropriately)
5) A user can explicitly specify -M xenpv.  If running under Xen, this 
would create a Xen PV guest.  If running on bare metal, Xenner would be 
used to present a Xen shim layer.  This should work with KVM 
acceleration or without KVM acceleration.  Bonus points if it works with 
kqemu too.

For 1-4, the user experience should not be different at all.  This of 
course doesn't mean that users would be forced to interact directly with 
QEMU or that QEMU cannot be running within a stub domain.  It would be 
good if these things could be automatically detected though.

Regards,

Anthony Liguori

> Daniel
>   

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
@ 2008-07-29 19:11               ` Anthony Liguori
  0 siblings, 0 replies; 65+ messages in thread
From: Anthony Liguori @ 2008-07-29 19:11 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: xen-devel, Gerd Hoffmann, qemu-devel

Daniel P. Berrange wrote:
> On Tue, Jul 29, 2008 at 08:32:11AM -0500, Anthony Liguori wrote:
>   
>> Why wouldn't the Xen backends be added by appropriate -net or -drive 
>> options?  For instance, qemu -drive file=foo.img,if=xen -net nic,model=xen
>>     
>
> That would work for certain setup tasks, but not all. In Xen's branch
> of QEMU, the machine init function has to set various hypervisor 
> parameters which aren't directly associated with QEMU command line
> args, and initialize things like the map-cache for > 4GB guest support.
>   

map-cache is one of those things I don't expect to ever get merged.

> In the Xennite work I had further stuff going on there too - more general
> initialization of Xenstore parameters - eg stuffing the name & uuid from
> QEMU's argv into right place in xenstore. 
>   

Ideally, I'd like to see Xen/KVM integration look like this:

1) Xen support is detected in configure (libxc et al) and conditionally 
enabled.
2) When running on bare metal, detect whether KVM acceleration is 
available, also detect if kqemu acceleration is available
3) When running under Xen, detect that Xen is available, and create a 
full virt domain
4) If a user specifies a type=xen device, it should Just Work provided 
you are in a Xen environment (erroring appropriately)
5) A user can explicitly specify -M xenpv.  If running under Xen, this 
would create a Xen PV guest.  If running on bare metal, Xenner would be 
used to present a Xen shim layer.  This should work with KVM 
acceleration or without KVM acceleration.  Bonus points if it works with 
kqemu too.

For 1-4, the user experience should not be different at all.  This of 
course doesn't mean that users would be forced to interact directly with 
QEMU or that QEMU cannot be running within a stub domain.  It would be 
good if these things could be automatically detected though.

Regards,

Anthony Liguori

> Daniel
>   

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
  2008-07-29 19:11               ` Anthony Liguori
@ 2008-07-29 21:36                 ` Gerd Hoffmann
  -1 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-29 21:36 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: xen-devel, qemu-devel

Anthony Liguori wrote:
> Daniel P. Berrange wrote:
>> That would work for certain setup tasks, but not all. In Xen's branch
>> of QEMU, the machine init function has to set various hypervisor
>> parameters which aren't directly associated with QEMU command line
>> args, and initialize things like the map-cache for > 4GB guest support.
> 
> map-cache is one of those things I don't expect to ever get merged.

And the need for that will go away over time IMHO.  If your Dom0 is
64bit you have no address space pressure and thus no need for mapcache.
 Given we have 32-on-64 and non-PAE Xen is depricated anyway there is
almost no reason to not run 64bit Xen and Dom0.

> Ideally, I'd like to see Xen/KVM integration look like this:
> 
> 1) Xen support is detected in configure (libxc et al) and conditionally
> enabled.
> 2) When running on bare metal, detect whether KVM acceleration is
> available, also detect if kqemu acceleration is available
> 3) When running under Xen, detect that Xen is available, and create a
> full virt domain
> 4) If a user specifies a type=xen device, it should Just Work provided
> you are in a Xen environment (erroring appropriately)
> 5) A user can explicitly specify -M xenpv.  If running under Xen, this
> would create a Xen PV guest.  If running on bare metal, Xenner would be
> used to present a Xen shim layer.  This should work with KVM
> acceleration or without KVM acceleration.  Bonus points if it works with
> kqemu too.

I'm surprised how well you can read my mind.
Yes, I wanna have the bonus points ;)

There are two additional points you didn't see though:

For (3) and (5) qemu should support two modes:  First, attach to an
existing domain.  This is how Xen works today.  And we want get rid of
the qemu-dm fork, right?  Second, optionally also create the domain,
like Xenite.

(4) should also just work when you are *not* in a Xen environment[1]

cheers,
  Gerd

[1]  It actually does, btw.  Code isn't ready yet for merging.
     Stay tuned ;)

-- 
http://kraxel.fedorapeople.org/xenner/

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
@ 2008-07-29 21:36                 ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-29 21:36 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: xen-devel, Daniel P. Berrange, qemu-devel

Anthony Liguori wrote:
> Daniel P. Berrange wrote:
>> That would work for certain setup tasks, but not all. In Xen's branch
>> of QEMU, the machine init function has to set various hypervisor
>> parameters which aren't directly associated with QEMU command line
>> args, and initialize things like the map-cache for > 4GB guest support.
> 
> map-cache is one of those things I don't expect to ever get merged.

And the need for that will go away over time IMHO.  If your Dom0 is
64bit you have no address space pressure and thus no need for mapcache.
 Given we have 32-on-64 and non-PAE Xen is depricated anyway there is
almost no reason to not run 64bit Xen and Dom0.

> Ideally, I'd like to see Xen/KVM integration look like this:
> 
> 1) Xen support is detected in configure (libxc et al) and conditionally
> enabled.
> 2) When running on bare metal, detect whether KVM acceleration is
> available, also detect if kqemu acceleration is available
> 3) When running under Xen, detect that Xen is available, and create a
> full virt domain
> 4) If a user specifies a type=xen device, it should Just Work provided
> you are in a Xen environment (erroring appropriately)
> 5) A user can explicitly specify -M xenpv.  If running under Xen, this
> would create a Xen PV guest.  If running on bare metal, Xenner would be
> used to present a Xen shim layer.  This should work with KVM
> acceleration or without KVM acceleration.  Bonus points if it works with
> kqemu too.

I'm surprised how well you can read my mind.
Yes, I wanna have the bonus points ;)

There are two additional points you didn't see though:

For (3) and (5) qemu should support two modes:  First, attach to an
existing domain.  This is how Xen works today.  And we want get rid of
the qemu-dm fork, right?  Second, optionally also create the domain,
like Xenite.

(4) should also just work when you are *not* in a Xen environment[1]

cheers,
  Gerd

[1]  It actually does, btw.  Code isn't ready yet for merging.
     Stay tuned ;)

-- 
http://kraxel.fedorapeople.org/xenner/

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
  2008-07-29 21:36                 ` Gerd Hoffmann
@ 2008-07-29 21:48                   ` Anthony Liguori
  -1 siblings, 0 replies; 65+ messages in thread
From: Anthony Liguori @ 2008-07-29 21:48 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Ian Jackson, xen-devel, qemu-devel, Samuel Thibault

Gerd Hoffmann wrote:
> Anthony Liguori wrote:
>   
>> map-cache is one of those things I don't expect to ever get merged.
>>     
>
> And the need for that will go away over time IMHO.  If your Dom0 is
> 64bit you have no address space pressure and thus no need for mapcache.
>  Given we have 32-on-64 and non-PAE Xen is depricated anyway there is
> almost no reason to not run 64bit Xen and Dom0.
>   

Right.

>> Ideally, I'd like to see Xen/KVM integration look like this:
>>
>> 1) Xen support is detected in configure (libxc et al) and conditionally
>> enabled.
>> 2) When running on bare metal, detect whether KVM acceleration is
>> available, also detect if kqemu acceleration is available
>> 3) When running under Xen, detect that Xen is available, and create a
>> full virt domain
>> 4) If a user specifies a type=xen device, it should Just Work provided
>> you are in a Xen environment (erroring appropriately)
>> 5) A user can explicitly specify -M xenpv.  If running under Xen, this
>> would create a Xen PV guest.  If running on bare metal, Xenner would be
>> used to present a Xen shim layer.  This should work with KVM
>> acceleration or without KVM acceleration.  Bonus points if it works with
>> kqemu too.
>>     
>
> I'm surprised how well you can read my mind.
>   

Scary, huh? :-)

> Yes, I wanna have the bonus points ;)
>
> There are two additional points you didn't see though:
>
> For (3) and (5) qemu should support two modes:  First, attach to an
> existing domain.  This is how Xen works today.  And we want get rid of
> the qemu-dm fork, right?  Second, optionally also create the domain,
> like Xenite.
>   

I have mixed feelings about this, but I don't think there's a way to 
support stub domains without this functionality.  Obviously, when you 
run QEMU within a stub domain, the guest domain has already been 
created.  Well, maybe it doesn't have to be that way but it seems most 
reasonable to do it that way.

> (4) should also just work when you are *not* in a Xen environment[1]
>   

I considered suggesting that but figured it would be too much.  I should 
have figured it was already working in some form :-)

So how does the upstream Xen community feel about all of this?  Is this 
a reasonable approach to merging Xen functionality into QEMU?

Regards,

Anthony Liguori

> cheers,
>   Gerd
>
> [1]  It actually does, btw.  Code isn't ready yet for merging.
>      Stay tuned ;)
>
>   

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
@ 2008-07-29 21:48                   ` Anthony Liguori
  0 siblings, 0 replies; 65+ messages in thread
From: Anthony Liguori @ 2008-07-29 21:48 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Ian Jackson, xen-devel, Daniel P. Berrange, qemu-devel, Samuel Thibault

Gerd Hoffmann wrote:
> Anthony Liguori wrote:
>   
>> map-cache is one of those things I don't expect to ever get merged.
>>     
>
> And the need for that will go away over time IMHO.  If your Dom0 is
> 64bit you have no address space pressure and thus no need for mapcache.
>  Given we have 32-on-64 and non-PAE Xen is depricated anyway there is
> almost no reason to not run 64bit Xen and Dom0.
>   

Right.

>> Ideally, I'd like to see Xen/KVM integration look like this:
>>
>> 1) Xen support is detected in configure (libxc et al) and conditionally
>> enabled.
>> 2) When running on bare metal, detect whether KVM acceleration is
>> available, also detect if kqemu acceleration is available
>> 3) When running under Xen, detect that Xen is available, and create a
>> full virt domain
>> 4) If a user specifies a type=xen device, it should Just Work provided
>> you are in a Xen environment (erroring appropriately)
>> 5) A user can explicitly specify -M xenpv.  If running under Xen, this
>> would create a Xen PV guest.  If running on bare metal, Xenner would be
>> used to present a Xen shim layer.  This should work with KVM
>> acceleration or without KVM acceleration.  Bonus points if it works with
>> kqemu too.
>>     
>
> I'm surprised how well you can read my mind.
>   

Scary, huh? :-)

> Yes, I wanna have the bonus points ;)
>
> There are two additional points you didn't see though:
>
> For (3) and (5) qemu should support two modes:  First, attach to an
> existing domain.  This is how Xen works today.  And we want get rid of
> the qemu-dm fork, right?  Second, optionally also create the domain,
> like Xenite.
>   

I have mixed feelings about this, but I don't think there's a way to 
support stub domains without this functionality.  Obviously, when you 
run QEMU within a stub domain, the guest domain has already been 
created.  Well, maybe it doesn't have to be that way but it seems most 
reasonable to do it that way.

> (4) should also just work when you are *not* in a Xen environment[1]
>   

I considered suggesting that but figured it would be too much.  I should 
have figured it was already working in some form :-)

So how does the upstream Xen community feel about all of this?  Is this 
a reasonable approach to merging Xen functionality into QEMU?

Regards,

Anthony Liguori

> cheers,
>   Gerd
>
> [1]  It actually does, btw.  Code isn't ready yet for merging.
>      Stay tuned ;)
>
>   

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
  2008-07-28 14:22   ` [Qemu-devel] " Anthony Liguori
@ 2008-07-30  9:20       ` Gerd Hoffmann
  2008-07-30  9:20       ` Gerd Hoffmann
  1 sibling, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-30  9:20 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: xen-devel, qemu-devel, Markus Armbruster

Anthony Liguori wrote:
> Gerd Hoffmann wrote:
>> +++ b/hw/xen-framebuffer.c
>> @@ -0,0 +1,925 @@
>> +/*
>> + *  xen paravirt framebuffer backend
> 
> No copyright?
> 

Well, qemu-dm's xenfb.c which I used as base didn't have one.
Guess most of this is Markus' code (CC'ed).

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 4/7] xen: add framebuffer backend driver
@ 2008-07-30  9:20       ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-30  9:20 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: xen-devel, qemu-devel, Markus Armbruster

Anthony Liguori wrote:
> Gerd Hoffmann wrote:
>> +++ b/hw/xen-framebuffer.c
>> @@ -0,0 +1,925 @@
>> +/*
>> + *  xen paravirt framebuffer backend
> 
> No copyright?
> 

Well, qemu-dm's xenfb.c which I used as base didn't have one.
Guess most of this is Markus' code (CC'ed).

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
  2008-07-28 14:41       ` Andreas Färber
  (?)
@ 2008-07-30  9:59       ` Gerd Hoffmann
  2008-08-01 14:57         ` Anthony Liguori
  -1 siblings, 1 reply; 65+ messages in thread
From: Gerd Hoffmann @ 2008-07-30  9:59 UTC (permalink / raw)
  To: Andreas Färber; +Cc: xen-devel, qemu-devel

  Hi,

>>> +/* 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++) {                \

>> There are similar conversion macros in the VGA code.  I don't know how
>> practical it would be to reuse them but it's at least worth looking at.
> 
> We created a file hw/pixel_ops.h to share code between vga.c and tcx.c,
> probably it can be extended to serve Xen as well.

Uhm, well, it isn't that easy.  Unifying that looks like a big job in
itself.  pixel_ops.h as-is isn't very helpful for xen-framebuffer.  The
xen-framebuffer macro isn't very useful to others.

What IMHO would be most useful is a set of generic conversion functions,
operating on scanline level, much like the ones created by
vga_template.h, but without dependencies on vga.c internals.

cheers,
  Gerd

-- 
http://kraxel.fedorapeople.org/xenner/

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
  2008-07-30  9:20       ` Gerd Hoffmann
@ 2008-07-30 16:31         ` Markus Armbruster
  -1 siblings, 0 replies; 65+ messages in thread
From: Markus Armbruster @ 2008-07-30 16:31 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: xen-devel, qemu-devel

Gerd Hoffmann <kraxel@redhat.com> writes:

> Anthony Liguori wrote:
>> Gerd Hoffmann wrote:
>>> +++ b/hw/xen-framebuffer.c
>>> @@ -0,0 +1,925 @@
>>> +/*
>>> + *  xen paravirt framebuffer backend
>> 
>> No copyright?
>> 
>
> Well, qemu-dm's xenfb.c which I used as base didn't have one.
> Guess most of this is Markus' code (CC'ed).
>
> cheers,
>   Gerd

Development history has been a bit more complicated than that :)

* Initial version: Anthony Liguori <anthony@codemonkey.ws>

  The link[*] to it has gone stale.  I don't think it had a copyright
  in the file that eventually mutated into the one we're discussing.

* Hacked into shape: me (xen-unstable changeset 12678).  No copyright
  in the source file.  Changeset carries:

  Signed-off-by: Markus Armbruster <armbru@redhat.com>
  Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>

* Additional changes (use "hg log tools/xenfb in xen-unstable" to list
  them) by:

  Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
  Signed-off-by: Gerd Hoffmann <kraxel@suse.de>
  Signed-off-by: Hirofumi Tsujimura <tsujimura.hirof@jp.fujitsu.com>
  Signed-off-by: Ian Campbell <ian.campbell@xensource.com>
  Signed-off-by: John Levon <john.levon@sun.com>
  Signed-off-by: Junko Ichino <ichino.junko@jp.fujitsu.com>
  Signed-off-by: Keir Fraser <keir@xensource.com>
  Signed-off-by: Markus Armbruster <armbru@redhat.com>
  Signed-off-by: Steven Smith <sos22@cam.ac.uk>
  Signed-off-by: Takanori Kasai <kasai.takanori@jp.fujitsu.com>
  Signed-off-by: Tsunehisa Doi <Doi.Tsunehisa@jp.fujitsu.com>

* Transformed from a separate program into a part of ioemu-dm:

  Signed-off-by: Daniel P. Berrange <berrange@redhat.com>

* Additional changes (use "hg log tools/xenfb" to list them) by:

  Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
  Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
  Signed-off-by: Keir Fraser <keir@xensource.com>
  Signed-off-by: Markus Armbruster <armbru@redhat.com>
  Signed-off-by: Pat Campbell <plc@novell.com>
  Signed-off-by: Samuel Thibault <samuel.thibault@eu.citrix.com>
  Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>

* Transformed to work on top of his common backend driver code in
  upstream QEMU:

  Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>

I guess a copyright note should include at least Anthony, me, Dan,
Pat, Gerd (in order of first major contribution).  Perhaps something
like:

 * Copyright (C) 2005-2006 Anthony Liguori <aliguori@us.ibm.com>
 * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>,
 *     Daniel P. Berrange <berrange@redhat.com>,
 *     Gerd Hoffmann <kraxel@redhat.com>
 * Copyright (C) 2008 Pat Campbell <plc@novell.com>

Apologies if I missed someone!

[*] http://www.cs.utexas.edu/users/aliguori/xen-vfb-20060124.bundle

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
@ 2008-07-30 16:31         ` Markus Armbruster
  0 siblings, 0 replies; 65+ messages in thread
From: Markus Armbruster @ 2008-07-30 16:31 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: xen-devel, qemu-devel, Anthony Liguori

Gerd Hoffmann <kraxel@redhat.com> writes:

> Anthony Liguori wrote:
>> Gerd Hoffmann wrote:
>>> +++ b/hw/xen-framebuffer.c
>>> @@ -0,0 +1,925 @@
>>> +/*
>>> + *  xen paravirt framebuffer backend
>> 
>> No copyright?
>> 
>
> Well, qemu-dm's xenfb.c which I used as base didn't have one.
> Guess most of this is Markus' code (CC'ed).
>
> cheers,
>   Gerd

Development history has been a bit more complicated than that :)

* Initial version: Anthony Liguori <anthony@codemonkey.ws>

  The link[*] to it has gone stale.  I don't think it had a copyright
  in the file that eventually mutated into the one we're discussing.

* Hacked into shape: me (xen-unstable changeset 12678).  No copyright
  in the source file.  Changeset carries:

  Signed-off-by: Markus Armbruster <armbru@redhat.com>
  Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>

* Additional changes (use "hg log tools/xenfb in xen-unstable" to list
  them) by:

  Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
  Signed-off-by: Gerd Hoffmann <kraxel@suse.de>
  Signed-off-by: Hirofumi Tsujimura <tsujimura.hirof@jp.fujitsu.com>
  Signed-off-by: Ian Campbell <ian.campbell@xensource.com>
  Signed-off-by: John Levon <john.levon@sun.com>
  Signed-off-by: Junko Ichino <ichino.junko@jp.fujitsu.com>
  Signed-off-by: Keir Fraser <keir@xensource.com>
  Signed-off-by: Markus Armbruster <armbru@redhat.com>
  Signed-off-by: Steven Smith <sos22@cam.ac.uk>
  Signed-off-by: Takanori Kasai <kasai.takanori@jp.fujitsu.com>
  Signed-off-by: Tsunehisa Doi <Doi.Tsunehisa@jp.fujitsu.com>

* Transformed from a separate program into a part of ioemu-dm:

  Signed-off-by: Daniel P. Berrange <berrange@redhat.com>

* Additional changes (use "hg log tools/xenfb" to list them) by:

  Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
  Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
  Signed-off-by: Keir Fraser <keir@xensource.com>
  Signed-off-by: Markus Armbruster <armbru@redhat.com>
  Signed-off-by: Pat Campbell <plc@novell.com>
  Signed-off-by: Samuel Thibault <samuel.thibault@eu.citrix.com>
  Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>

* Transformed to work on top of his common backend driver code in
  upstream QEMU:

  Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>

I guess a copyright note should include at least Anthony, me, Dan,
Pat, Gerd (in order of first major contribution).  Perhaps something
like:

 * Copyright (C) 2005-2006 Anthony Liguori <aliguori@us.ibm.com>
 * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>,
 *     Daniel P. Berrange <berrange@redhat.com>,
 *     Gerd Hoffmann <kraxel@redhat.com>
 * Copyright (C) 2008 Pat Campbell <plc@novell.com>

Apologies if I missed someone!

[*] http://www.cs.utexas.edu/users/aliguori/xen-vfb-20060124.bundle

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
  2008-07-30  9:59       ` [Qemu-devel] " Gerd Hoffmann
@ 2008-08-01 14:57         ` Anthony Liguori
  0 siblings, 0 replies; 65+ messages in thread
From: Anthony Liguori @ 2008-08-01 14:57 UTC (permalink / raw)
  To: qemu-devel; +Cc: Andreas Färber, xen-devel

Gerd Hoffmann wrote:
>   Hi,
>
>   
>>>> +/* 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++) {                \
>>>>         
>
>   
>>> There are similar conversion macros in the VGA code.  I don't know how
>>> practical it would be to reuse them but it's at least worth looking at.
>>>       
>> We created a file hw/pixel_ops.h to share code between vga.c and tcx.c,
>> probably it can be extended to serve Xen as well.
>>     
>
> Uhm, well, it isn't that easy.  Unifying that looks like a big job in
> itself.  pixel_ops.h as-is isn't very helpful for xen-framebuffer.  The
> xen-framebuffer macro isn't very useful to others.
>   

Okay, the xen-framebuffer macros aren't that big so it's not a huge 
deal.  Was worth looking into though.  Thanks!

Regards,

Anthony Liguori

> What IMHO would be most useful is a set of generic conversion functions,
> operating on scanline level, much like the ones created by
> vga_template.h, but without dependencies on vga.c internals.
>
> cheers,
>   Gerd
>
>   

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
  2008-07-30 16:31         ` Markus Armbruster
  (?)
@ 2008-08-01 15:05         ` Anthony Liguori
  -1 siblings, 0 replies; 65+ messages in thread
From: Anthony Liguori @ 2008-08-01 15:05 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: xen-devel, Gerd Hoffmann, qemu-devel

Markus Armbruster wrote:
> Gerd Hoffmann <kraxel@redhat.com> writes:
>
>   
> I guess a copyright note should include at least Anthony, me, Dan,
> Pat, Gerd (in order of first major contribution).  Perhaps something
> like:
>
>  * Copyright (C) 2005-2006 Anthony Liguori <aliguori@us.ibm.com>
>   

Please add my copyright as:

* Copyright IBM, Corp. 2005-2006
*
* Authors:
*   Anthony Liguori <aliguori@us.ibm.com>

Regards,

Anthony Liguori

>  * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>,
>  *     Daniel P. Berrange <berrange@redhat.com>,
>  *     Gerd Hoffmann <kraxel@redhat.com>
>  * Copyright (C) 2008 Pat Campbell <plc@novell.com>
>
> Apologies if I missed someone!
>
> [*] http://www.cs.utexas.edu/users/aliguori/xen-vfb-20060124.bundle
>   

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 3/7] xen: add console backend driver.
  2008-10-28 17:16   ` Blue Swirl
@ 2008-10-29 10:53     ` Gerd Hoffmann
  0 siblings, 0 replies; 65+ messages in thread
From: Gerd Hoffmann @ 2008-10-29 10:53 UTC (permalink / raw)
  To: qemu-devel; +Cc: xen-devel

Blue Swirl wrote:
> On 10/28/08, Gerd Hoffmann <kraxel@redhat.com> wrote:
>> 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.
> 
>>  + *  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
> 
> As if anyone cares: FSF address in this patch and others should probably be
> 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

Hmm, would it be ok to just scratch the whole paragraph from the
comment, so we avoid stale address info in the first place?  Shouldn't
be needed to make clear the source code is GPL, but IANAL ...

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 3/7] xen: add console backend driver.
  2008-10-28 12:23 ` [Qemu-devel] [PATCH 3/7] xen: add console backend driver Gerd Hoffmann
@ 2008-10-28 17:16   ` Blue Swirl
  2008-10-29 10:53     ` Gerd Hoffmann
  0 siblings, 1 reply; 65+ messages in thread
From: Blue Swirl @ 2008-10-28 17:16 UTC (permalink / raw)
  To: qemu-devel; +Cc: xen-devel, Gerd Hoffmann

On 10/28/08, Gerd Hoffmann <kraxel@redhat.com> wrote:
> 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.

>  + *  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

As if anyone cares: FSF address in this patch and others should probably be
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

^ permalink raw reply	[flat|nested] 65+ messages in thread

* [Qemu-devel] [PATCH 3/7] xen: add console backend driver.
  2008-10-28 12:23 [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu Gerd Hoffmann
@ 2008-10-28 12:23 ` Gerd Hoffmann
  2008-10-28 17:16   ` Blue Swirl
  0 siblings, 1 reply; 65+ messages in thread
From: Gerd Hoffmann @ 2008-10-28 12:23 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

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 |    3 +
 4 files changed, 283 insertions(+), 0 deletions(-)
 create mode 100644 hw/xen_console.c

diff --git a/Makefile.target b/Makefile.target
index c18cbc6..50f1dca 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -631,6 +631,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 67f997f..fca2e09 100644
--- a/hw/xen_machine_pv.c
+++ b/hw/xen_machine_pv.c
@@ -63,6 +63,9 @@ static int xen_init_pv(DisplayState *ds)
     if (rc < 0)
         return rc;
 
+    /* xenbus backend drivers */
+    xen_be_register("console", &xen_console_ops);
+
     return 0;
 }
 
-- 
1.5.6.5

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 3/7] xen: add console backend driver.
  2008-08-04 18:15     ` Gerd Hoffmann
@ 2008-08-04 20:47       ` Blue Swirl
  0 siblings, 0 replies; 65+ messages in thread
From: Blue Swirl @ 2008-08-04 20:47 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: xen-devel, qemu-devel

On 8/4/08, Gerd Hoffmann <kraxel@redhat.com> wrote:
> Blue Swirl wrote:
>  > On 8/4/08, Gerd Hoffmann <kraxel@redhat.com> wrote:
>  >>  +       buffer->data = realloc(buffer->data, buffer->capacity);
>  >
>  > On Qemu, instead of malloc,  qemu_malloc should be used to allocate
>  > memory. Though there isn't qemu_realloc.
>
>
> Should I add qemu_realloc then?

That would be nice, but changing also all reallocs to qemu_realloc
calls for a new patch.

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 3/7] xen: add console backend driver.
  2008-08-04 16:52   ` Blue Swirl
@ 2008-08-04 18:15     ` Gerd Hoffmann
  2008-08-04 20:47       ` Blue Swirl
  0 siblings, 1 reply; 65+ messages in thread
From: Gerd Hoffmann @ 2008-08-04 18:15 UTC (permalink / raw)
  To: Blue Swirl; +Cc: xen-devel, qemu-devel

Blue Swirl wrote:
> On 8/4/08, Gerd Hoffmann <kraxel@redhat.com> wrote:
>>  +       buffer->data = realloc(buffer->data, buffer->capacity);
> 
> On Qemu, instead of malloc,  qemu_malloc should be used to allocate
> memory. Though there isn't qemu_realloc.

Should I add qemu_realloc then?

>>  +    if (!type || 0 != strcmp(type, "ioemu")) {
> 
> Shouldn't this be "qemu"?

No, "ioemu" is correct.  xend writes that value into xenstore.

cheers,
  Gerd

-- 
http://kraxel.fedorapeople.org/xenner/

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Qemu-devel] [PATCH 3/7] xen: add console backend driver.
  2008-08-04 15:50 ` [Qemu-devel] [PATCH 3/7] xen: add console backend driver Gerd Hoffmann
@ 2008-08-04 16:52   ` Blue Swirl
  2008-08-04 18:15     ` Gerd Hoffmann
  0 siblings, 1 reply; 65+ messages in thread
From: Blue Swirl @ 2008-08-04 16:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: xen-devel, Gerd Hoffmann

On 8/4/08, Gerd Hoffmann <kraxel@redhat.com> wrote:
>  +       buffer->data = realloc(buffer->data, buffer->capacity);

On Qemu, instead of malloc,  qemu_malloc should be used to allocate
memory. Though there isn't qemu_realloc.

>  +    if (!type || 0 != strcmp(type, "ioemu")) {

Shouldn't this be "qemu"?

^ permalink raw reply	[flat|nested] 65+ messages in thread

* [Qemu-devel] [PATCH 3/7] xen: add console backend driver.
  2008-08-04 15:50 [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu Gerd Hoffmann
@ 2008-08-04 15:50 ` Gerd Hoffmann
  2008-08-04 16:52   ` Blue Swirl
  0 siblings, 1 reply; 65+ messages in thread
From: Gerd Hoffmann @ 2008-08-04 15:50 UTC (permalink / raw)
  To: qemu-devel, xen-devel; +Cc: Gerd Hoffmann

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.c |    3 +
 4 files changed, 277 insertions(+), 0 deletions(-)
 create mode 100644 hw/xen-console.c

diff --git a/Makefile.target b/Makefile.target
index 65f4910..f61458c 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -517,6 +517,7 @@ endif
 
 # xen backend driver support
 XEN_OBJS := xen-machine.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 13b3190..61c37ac 100644
--- a/hw/xen-backend.h
+++ b/hw/xen-backend.h
@@ -84,4 +84,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 */
+struct XenDevOps xen_console_ops;      /* xen_console.c     */
+
 #endif /* QEMU_XEN_BACKEND_H */
diff --git a/hw/xen-console.c b/hw/xen-console.c
new file mode 100644
index 0000000..57bd3a1
--- /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., 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[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 = 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 = 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;
+    len = qemu_chr_write(con->chr, con->buffer.data + con->buffer.consumed,
+			 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;
+
+    if (!serial_hds[con->xendev.dev]) {
+	xen_be_printf(xendev, 1, "serial line %d not configured\n", con->xendev.dev);
+	return -1;
+    }
+
+    /* setup */
+    snprintf(con->console, sizeof(con->console),
+	     "/local/domain/%d/console", con->xendev.dom);
+    con->chr = serial_hds[con->xendev.dev];
+
+    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;
+    }
+
+    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);
+    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);
+
+    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.c b/hw/xen-machine.c
index f0ebd60..de22cdb 100644
--- a/hw/xen-machine.c
+++ b/hw/xen-machine.c
@@ -97,6 +97,9 @@ static int xen_init_pv(DisplayState *ds)
     if (rc < 0)
         return rc;
 
+    /* xenbus backend drivers */
+    xen_be_register("console", &xen_console_ops);
+
     return 0;
 }
 
-- 
1.5.5.1

^ permalink raw reply related	[flat|nested] 65+ messages in thread

end of thread, other threads:[~2008-10-29 10:53 UTC | newest]

Thread overview: 65+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-07-28 13:17 [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu Gerd Hoffmann
2008-07-28 13:17 ` Gerd Hoffmann
2008-07-28 13:17 ` [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support Gerd Hoffmann
2008-07-28 13:17   ` Gerd Hoffmann
2008-07-28 14:04   ` [Qemu-devel] " Anthony Liguori
2008-07-28 14:52     ` Gerd Hoffmann
2008-07-29  8:10       ` [Xen-devel] " Daniel P. Berrange
2008-07-29  8:10         ` Daniel P. Berrange
2008-07-29 13:32         ` [Xen-devel] " Anthony Liguori
2008-07-29 13:32           ` Anthony Liguori
2008-07-29 14:24           ` [Xen-devel] " Daniel P. Berrange
2008-07-29 14:24             ` Daniel P. Berrange
2008-07-29 19:11             ` [Xen-devel] " Anthony Liguori
2008-07-29 19:11               ` Anthony Liguori
2008-07-29 21:36               ` [Xen-devel] " Gerd Hoffmann
2008-07-29 21:36                 ` Gerd Hoffmann
2008-07-29 21:48                 ` [Xen-devel] " Anthony Liguori
2008-07-29 21:48                   ` Anthony Liguori
2008-07-29 14:32           ` [Xen-devel] " Gerd Hoffmann
2008-07-29 14:32             ` Gerd Hoffmann
2008-07-28 23:14     ` [Xen-devel] " Samuel Thibault
2008-07-28 23:14       ` Samuel Thibault
2008-07-29  7:38       ` [Xen-devel] " Gerd Hoffmann
2008-07-29  7:38         ` Gerd Hoffmann
2008-07-29  8:12         ` [Xen-devel] " Daniel P. Berrange
2008-07-29  8:12           ` Daniel P. Berrange
2008-07-29  8:55           ` [Xen-devel] " Gerd Hoffmann
2008-07-29  8:55             ` Gerd Hoffmann
2008-07-28 13:17 ` [Qemu-devel] [PATCH 2/7] xen: backend driver core Gerd Hoffmann
2008-07-28 13:17   ` Gerd Hoffmann
2008-07-28 14:13   ` [Qemu-devel] " Anthony Liguori
2008-07-28 15:51     ` Gerd Hoffmann
2008-07-28 13:17 ` [Qemu-devel] [PATCH 3/7] xen: add console backend driver Gerd Hoffmann
2008-07-28 13:17   ` Gerd Hoffmann
2008-07-28 14:17   ` [Qemu-devel] " Anthony Liguori
2008-07-28 15:43     ` Gerd Hoffmann
2008-07-28 19:04       ` Anthony Liguori
2008-07-28 13:17 ` [Qemu-devel] [PATCH 4/7] xen: add framebuffer " Gerd Hoffmann
2008-07-28 13:17   ` Gerd Hoffmann
2008-07-28 14:22   ` [Qemu-devel] " Anthony Liguori
2008-07-28 14:41     ` Andreas Färber
2008-07-28 14:41       ` Andreas Färber
2008-07-30  9:59       ` [Qemu-devel] " Gerd Hoffmann
2008-08-01 14:57         ` Anthony Liguori
2008-07-30  9:20     ` Gerd Hoffmann
2008-07-30  9:20       ` Gerd Hoffmann
2008-07-30 16:31       ` [Qemu-devel] " Markus Armbruster
2008-07-30 16:31         ` Markus Armbruster
2008-08-01 15:05         ` Anthony Liguori
2008-07-28 13:17 ` [Qemu-devel] [PATCH 5/7] xen: add block device " Gerd Hoffmann
2008-07-28 13:17   ` Gerd Hoffmann
2008-07-28 14:25   ` [Qemu-devel] " Anthony Liguori
2008-07-28 13:17 ` [Qemu-devel] [PATCH 6/7] xen: add net " Gerd Hoffmann
2008-07-28 13:17   ` Gerd Hoffmann
2008-07-28 14:27   ` [Qemu-devel] " Anthony Liguori
2008-07-28 15:45     ` Gerd Hoffmann
2008-07-28 13:17 ` [Qemu-devel] [PATCH 7/7] xen: blk & nic configuration via cmd line Gerd Hoffmann
2008-07-28 13:17   ` Gerd Hoffmann
2008-08-04 15:50 [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu Gerd Hoffmann
2008-08-04 15:50 ` [Qemu-devel] [PATCH 3/7] xen: add console backend driver Gerd Hoffmann
2008-08-04 16:52   ` Blue Swirl
2008-08-04 18:15     ` Gerd Hoffmann
2008-08-04 20:47       ` Blue Swirl
2008-10-28 12:23 [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu Gerd Hoffmann
2008-10-28 12:23 ` [Qemu-devel] [PATCH 3/7] xen: add console backend driver Gerd Hoffmann
2008-10-28 17:16   ` Blue Swirl
2008-10-29 10:53     ` Gerd Hoffmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.