>From aa19020667d6ccab98eab2077042785e4a8559df Mon Sep 17 00:00:00 2001
From: Gerd Hoffmann
Date: Wed, 22 Apr 2009 12:56:04 +0200
Subject: [PATCH] xenner: event channel implementation.
---
Makefile.target | 19 ++-
configure | 18 ++-
hw/xen.h | 2 +
hw/xen_backend.h | 1 +
hw/xen_interfaces.c | 53 ++++++
hw/xen_interfaces.h | 31 ++++
hw/xen_machine_pv.c | 1 +
hw/xen_redirect.h | 18 ++
hw/xenner_libxc_evtchn.c | 401 ++++++++++++++++++++++++++++++++++++++++++++++
monitor.c | 5 +
10 files changed, 541 insertions(+), 8 deletions(-)
create mode 100644 hw/xen_interfaces.c
create mode 100644 hw/xen_interfaces.h
create mode 100644 hw/xen_redirect.h
create mode 100644 hw/xenner_libxc_evtchn.c
diff --git a/Makefile.target b/Makefile.target
index 82ada5a..4195026 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -561,14 +561,23 @@ ifdef CONFIG_BLUEZ
LIBS += $(CONFIG_BLUEZ_LIBS)
endif
-# xen backend driver support
-XEN_OBJS := xen_machine_pv.o xen_backend.o xen_devconfig.o xen_domainbuild.o
-XEN_OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o
-ifeq ($(CONFIG_XEN), yes)
- OBJS += $(XEN_OBJS)
+# xen backend drivers (shared by xenner+xen)
+ifneq ($(CONFIG_XEN)$(CONFIG_XENNER),)
+ OBJS += xen_machine_pv.o xen_backend.o xen_devconfig.o xen_interfaces.o
+ OBJS += xen_console.o xenfb.o xen_disk.o xen_nic.o
+endif
+
+# xen support
+ifeq ($(CONFIG_XEN),yes)
+ OBJS += xen_domainbuild.o
LIBS += $(XEN_LIBS)
endif
+# xenner support
+ifeq ($(CONFIG_XENNER),yes)
+ OBJS += xenner_libxc_evtchn.o
+endif
+
# SCSI layer
OBJS+= lsi53c895a.o esp.o
diff --git a/configure b/configure
index 1cbeabc..b28a664 100755
--- a/configure
+++ b/configure
@@ -194,6 +194,7 @@ blobs="yes"
fdt="yes"
sdl_x11="no"
xen="yes"
+xenner="yes"
pkgversion=""
# OS specific
@@ -424,7 +425,9 @@ for opt do
;;
--disable-kqemu) kqemu="no"
;;
- --disable-xen) xen="no"
+ --disable-xen) xen="no"; xenner="no"
+ ;;
+ --disable-xenner) xenner="no"
;;
--disable-brlapi) brlapi="no"
;;
@@ -596,6 +599,7 @@ echo " --audio-card-list=LIST set list of emulated audio cards [$audio_card_l
echo " Available cards: $audio_possible_cards"
echo " --enable-mixemu enable mixer emulation"
echo " --disable-xen disable xen backend driver support"
+echo " --disable-xenner disable xenner (xen emulation) support"
echo " --disable-brlapi disable BrlAPI"
echo " --disable-vnc-tls disable TLS encryption for VNC server"
echo " --disable-vnc-sasl disable SASL encryption for VNC server"
@@ -814,9 +818,10 @@ else
fi
##########################################
-# xen probe
+# probe for xen libs and includes
+# note: xenner included here as it needs the headers too.
-if test "$xen" = "yes" ; then
+if test "$xen" = "yes" -o "$xenner" = "yes"; then
cat > $TMPC <
#include
@@ -826,6 +831,7 @@ EOF
:
else
xen="no"
+ xenner="no"
fi
fi
@@ -1330,6 +1336,7 @@ if test -n "$sparc_cpu"; then
fi
echo "kqemu support $kqemu"
echo "xen support $xen"
+echo "xenner support $xenner"
echo "brlapi support $brlapi"
echo "Documentation $build_docs"
[ ! -z "$uname_release" ] && \
@@ -1825,6 +1832,11 @@ case "$target_cpu" in
echo "CONFIG_XEN=yes" >> $config_mak
echo "#define CONFIG_XEN 1" >> $config_h
fi
+ if test "$xenner" = "yes" -a "$target_softmmu" = "yes";
+ then
+ echo "CONFIG_XENNER=yes" >> $config_mak
+ echo "#define CONFIG_XENNER 1" >> $config_h
+ fi
;;
x86_64)
echo "TARGET_ARCH=x86_64" >> $config_mak
diff --git a/hw/xen.h b/hw/xen.h
index 3c8da41..525a179 100644
--- a/hw/xen.h
+++ b/hw/xen.h
@@ -17,4 +17,6 @@ enum xen_mode {
extern uint32_t xen_domid;
extern enum xen_mode xen_mode;
+void do_info_evtchn(Monitor *mon);
+
#endif /* QEMU_HW_XEN_H */
diff --git a/hw/xen_backend.h b/hw/xen_backend.h
index 4dbfdb4..2011dee 100644
--- a/hw/xen_backend.h
+++ b/hw/xen_backend.h
@@ -2,6 +2,7 @@
#define QEMU_HW_XEN_BACKEND_H 1
#include "xen_common.h"
+#include "xen_redirect.h"
#include "sysemu.h"
#include "net.h"
#include "block_int.h"
diff --git a/hw/xen_interfaces.c b/hw/xen_interfaces.c
new file mode 100644
index 0000000..573c674
--- /dev/null
+++ b/hw/xen_interfaces.c
@@ -0,0 +1,53 @@
+#include
+
+#include "hw.h"
+#include "xen.h"
+#include "xen_interfaces.h"
+
+#ifdef CONFIG_XEN
+
+static int xc_evtchn_domid(int handle, int domid)
+{
+ return -1;
+}
+
+static struct XenEvtOps xc_evtchn_xen = {
+ .open = xc_evtchn_open,
+ .domid = xc_evtchn_domid,
+ .close = xc_evtchn_close,
+ .fd = xc_evtchn_fd,
+ .notify = xc_evtchn_notify,
+ .bind_unbound_port = xc_evtchn_bind_unbound_port,
+ .bind_interdomain = xc_evtchn_bind_interdomain,
+ .bind_virq = xc_evtchn_bind_virq,
+ .unbind = xc_evtchn_unbind,
+ .pending = xc_evtchn_pending,
+ .unmask = xc_evtchn_unmask,
+};
+
+#endif
+
+struct XenEvtOps xc_evtchn;
+
+void xen_interfaces_init(void)
+{
+ switch (xen_mode) {
+ case XEN_ATTACH:
+ case XEN_CREATE:
+#ifdef CONFIG_XEN
+ xc_evtchn = xc_evtchn_xen;
+#else
+ fprintf(stderr, "ERROR: Compiled without xen support, sorry.\n");
+ exit(1);
+#endif
+ break;
+ case XEN_EMULATE:
+#ifdef CONFIG_XENNER
+ xc_evtchn = xc_evtchn_xenner;
+#else
+ fprintf(stderr, "ERROR: Compiled without xenner support, sorry.\n");
+ exit(1);
+#endif
+ break;
+ }
+}
diff --git a/hw/xen_interfaces.h b/hw/xen_interfaces.h
new file mode 100644
index 0000000..119e0e0
--- /dev/null
+++ b/hw/xen_interfaces.h
@@ -0,0 +1,31 @@
+#ifndef QEMU_HW_XEN_INTERFACES_H
+#define QEMU_HW_XEN_INTERFACES_H 1
+
+/* ------------------------------------------------------------- */
+/* xen event channel interface */
+
+struct XenEvtOps {
+ int (*open)(void);
+ int (*domid)(int xce_handle, int domid);
+ int (*close)(int xce_handle);
+ int (*fd)(int xce_handle);
+ int (*notify)(int xce_handle, evtchn_port_t port);
+ evtchn_port_or_error_t (*bind_unbound_port)(int xce_handle, int domid);
+ evtchn_port_or_error_t (*bind_interdomain)(int xce_handle, int domid,
+ evtchn_port_t remote_port);
+ evtchn_port_or_error_t (*bind_virq)(int xce_handle, unsigned int virq);
+ int (*unbind)(int xce_handle, evtchn_port_t port);
+ evtchn_port_or_error_t (*pending)(int xce_handle);
+ int (*unmask)(int xce_handle, evtchn_port_t port);
+};
+extern struct XenEvtOps xc_evtchn;
+
+/* ------------------------------------------------------------- */
+
+#ifdef CONFIG_XENNER
+extern struct XenEvtOps xc_evtchn_xenner;
+#endif
+
+void xen_interfaces_init(void);
+
+#endif /* QEMU_HW_XEN_INTERFACES_H */
diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c
index 58209b8..0476ebe 100644
--- a/hw/xen_machine_pv.c
+++ b/hw/xen_machine_pv.c
@@ -54,6 +54,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size,
env->halted = 1;
/* Initialize backend core & drivers */
+ xen_interfaces_init();
if (xen_be_init() != 0) {
fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__);
exit(1);
diff --git a/hw/xen_redirect.h b/hw/xen_redirect.h
new file mode 100644
index 0000000..ea08fd9
--- /dev/null
+++ b/hw/xen_redirect.h
@@ -0,0 +1,18 @@
+#ifndef QEMU_HW_XEN_REDIRECT_H
+#define QEMU_HW_XEN_REDIRECT_H 1
+
+#include "xen_interfaces.h"
+
+/* xen event channel interface */
+#define xc_evtchn_open xc_evtchn.open
+#define xc_evtchn_close xc_evtchn.close
+#define xc_evtchn_fd xc_evtchn.fd
+#define xc_evtchn_notify xc_evtchn.notify
+#define xc_evtchn_bind_unbound_port xc_evtchn.bind_unbound_port
+#define xc_evtchn_bind_interdomain xc_evtchn.bind_interdomain
+#define xc_evtchn_bind_virq xc_evtchn.bind_virq
+#define xc_evtchn_unbind xc_evtchn.unbind
+#define xc_evtchn_pending xc_evtchn.pending
+#define xc_evtchn_unmask xc_evtchn.unmask
+
+#endif /* QEMU_HW_XEN_REDIRECT_H */
diff --git a/hw/xenner_libxc_evtchn.c b/hw/xenner_libxc_evtchn.c
new file mode 100644
index 0000000..486674b
--- /dev/null
+++ b/hw/xenner_libxc_evtchn.c
@@ -0,0 +1,401 @@
+#include
+#include
+
+#include "hw.h"
+#include "qemu-log.h"
+#include "console.h"
+#include "monitor.h"
+#include "xen.h"
+#include "xen_interfaces.h"
+
+/* ------------------------------------------------------------- */
+
+struct evtpriv;
+
+struct port {
+ struct evtpriv *priv;
+ struct port *peer;
+ int port;
+ int pending;
+ int count_snd;
+ int count_fwd;
+ int count_msg;
+};
+
+struct domain {
+ int domid;
+ struct port p[NR_EVENT_CHANNELS];
+};
+static struct domain dom0; /* host */
+static struct domain domU; /* guest */
+
+struct evtpriv {
+ int fd_read, fd_write;
+ struct domain *domain;
+ int ports;
+ int pending;
+ TAILQ_ENTRY(evtpriv) list;
+};
+static TAILQ_HEAD(evtpriv_head, evtpriv) privs = TAILQ_HEAD_INITIALIZER(privs);
+
+static int debug = 1;
+
+/* ------------------------------------------------------------- */
+
+static struct evtpriv *getpriv(int handle)
+{
+ struct evtpriv *priv;
+
+ TAILQ_FOREACH(priv, &privs, list) {
+ if (priv->fd_read == handle)
+ return priv;
+ }
+ return NULL;
+}
+
+static struct domain *get_domain(int domid)
+{
+ if (domid == 0)
+ return &dom0;
+ if (domU.domid == 0)
+ domU.domid = domid;
+ assert(domU.domid == domid);
+ return &domU;
+}
+
+static struct port *alloc_port(struct evtpriv *priv, const char *reason)
+{
+ struct port *p = NULL;
+ int i;
+
+ for (i = 1; i < NR_EVENT_CHANNELS; i++) {
+#if 1
+ /* debug hack */
+#define EA_START 20
+ if (priv->domain->domid && i < EA_START)
+ i = EA_START;
+#endif
+ if (priv->domain->p[i].priv != NULL)
+ continue;
+ p = priv->domain->p+i;
+ p->port = i;
+ p->priv = priv;
+ p->count_snd = 0;
+ p->count_fwd = 0;
+ p->count_msg = 1;
+ priv->ports++;
+ if (debug)
+ qemu_log("xen ev:%3d: alloc port %d, domain %d (%s)\n",
+ priv->fd_read, p->port, priv->domain->domid, reason);
+ return p;
+ }
+ return NULL;
+}
+
+static void bind_port_peer(struct port *p, int domid, int port)
+{
+ struct domain *domain;
+ struct port *o;
+ const char *msg = "ok";
+
+ domain = get_domain(domid);
+ o = domain->p+port;
+ if (!o->priv) {
+ msg = "peer not allocated";
+ } else if (o->peer) {
+ msg = "peer already bound";
+ } else if (p->peer) {
+ msg = "port already bound";
+ } else {
+ o->peer = p;
+ p->peer = o;
+ }
+ if (debug)
+ qemu_log("xen ev:%3d: bind port %d domain %d <-> port %d domain %d : %s\n",
+ p->priv->fd_read,
+ p->port, p->priv->domain->domid,
+ port, domid, msg);
+}
+
+static void unbind_port(struct port *p)
+{
+ struct port *o;
+
+ o = p->peer;
+ if (o) {
+ if (debug)
+ fprintf(stderr,"xen ev:%3d: unbind port %d domain %d <-> port %d domain %d\n",
+ p->priv->fd_read,
+ p->port, p->priv->domain->domid,
+ o->port, o->priv->domain->domid);
+ o->peer = NULL;
+ p->peer = NULL;
+ }
+}
+
+static void notify_send_peer(struct port *peer)
+{
+ uint32_t evtchn = peer->port;
+
+ peer->count_snd++;
+ if (peer->pending)
+ return;
+
+ write(peer->priv->fd_write, &evtchn, sizeof(evtchn));
+ peer->count_fwd++;
+ peer->pending++;
+ peer->priv->pending++;
+}
+
+static void notify_port(struct port *p)
+{
+ if (p->peer) {
+ notify_send_peer(p->peer);
+ if (debug && p->peer->count_snd >= p->peer->count_msg) {
+ qemu_log("xen ev:%3d: notify port %d domain %d -> port %d domain %d | counts %d/%d\n",
+ p->priv->fd_read, p->port, p->priv->domain->domid,
+ p->peer->port, p->peer->priv->domain->domid,
+ p->peer->count_fwd, p->peer->count_snd);
+ p->peer->count_msg *= 10;
+ }
+ } else {
+ if (debug)
+ fprintf(stderr,"xen ev:%3d: notify port %d domain %d -> unconnected\n",
+ p->priv->fd_read, p->port, p->priv->domain->domid);
+ }
+}
+
+static void unmask_port(struct port *p)
+{
+ /* nothing to do */
+}
+
+static void release_port(struct port *p)
+{
+ if (debug)
+ fprintf(stderr,"xen ev:%3d: release port %d, domain %d\n",
+ p->priv->fd_read, p->port, p->priv->domain->domid);
+ unbind_port(p);
+ p->priv->ports--;
+ p->port = 0;
+ p->priv = 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static int qemu_open(void)
+{
+ struct evtpriv *priv;
+ int fd[2];
+
+ priv = qemu_mallocz(sizeof(*priv));
+ TAILQ_INSERT_TAIL(&privs, priv, list);
+
+ if (pipe(fd) < 0)
+ goto err;
+ priv->fd_read = fd[0];
+ priv->fd_write = fd[1];
+ fcntl(priv->fd_read,F_SETFL,O_NONBLOCK);
+
+ priv->domain = get_domain(0);
+ return priv->fd_read;
+
+err:
+ qemu_free(priv);
+ return -1;
+}
+
+static int qemu_close(int handle)
+{
+ struct evtpriv *priv = getpriv(handle);
+ struct port *p;
+ int i;
+
+ if (!priv)
+ return -1;
+
+ for (i = 1; i < NR_EVENT_CHANNELS; i++) {
+ p = priv->domain->p+i;
+ if (priv != p->priv)
+ continue;
+ release_port(p);
+ }
+
+ close(priv->fd_read);
+ close(priv->fd_write);
+ TAILQ_REMOVE(&privs, priv, list);
+ qemu_free(priv);
+ return 0;
+}
+
+static int qemu_fd(int handle)
+{
+ struct evtpriv *priv = getpriv(handle);
+
+ if (!priv)
+ return -1;
+ return priv->fd_read;
+}
+
+static int qemu_notify(int handle, evtchn_port_t port)
+{
+ struct evtpriv *priv = getpriv(handle);
+ struct port *p;
+
+ if (!priv)
+ return -1;
+ if (port >= NR_EVENT_CHANNELS)
+ return -1;
+ p = priv->domain->p + port;
+ notify_port(p);
+ return -1;
+}
+
+static evtchn_port_or_error_t qemu_bind_unbound_port(int handle, int domid)
+{
+ struct evtpriv *priv = getpriv(handle);
+ struct port *p;
+
+ if (!priv)
+ return -1;
+ p = alloc_port(priv, "unbound");
+ if (!p)
+ return -1;
+ return p->port;
+}
+
+static evtchn_port_or_error_t qemu_bind_interdomain(int handle, int domid,
+ evtchn_port_t remote_port)
+{
+ struct evtpriv *priv = getpriv(handle);
+ struct port *p;
+
+ if (!priv)
+ return -1;
+ if (remote_port >= NR_EVENT_CHANNELS)
+ return -1;
+ p = alloc_port(priv, "interdomain");
+ if (!p)
+ return -1;
+ bind_port_peer(p, domid, remote_port);
+ return p->port;
+}
+
+static evtchn_port_or_error_t qemu_bind_virq(int handle, unsigned int virq)
+{
+ struct evtpriv *priv = getpriv(handle);
+ struct port *p;
+
+ if (!priv)
+ return -1;
+ p = alloc_port(priv, "virq");
+ if (!p)
+ return -1;
+ /*
+ * Note: port not linked here, we only allocate some port.
+ */
+ return p->port;
+}
+
+static int qemu_unbind(int handle, evtchn_port_t port)
+{
+ struct evtpriv *priv = getpriv(handle);
+ struct port *p;
+
+ if (!priv)
+ return -1;
+ if (port >= NR_EVENT_CHANNELS)
+ return -1;
+ p = priv->domain->p + port;
+ unbind_port(p);
+ release_port(p);
+ return 0;
+}
+
+static evtchn_port_or_error_t qemu_pending(int handle)
+{
+ struct evtpriv *priv = getpriv(handle);
+ uint32_t evtchn;
+ int rc;
+
+ if (!priv)
+ return -1;
+ rc = read(priv->fd_read, &evtchn, sizeof(evtchn));
+ if (rc != sizeof(evtchn))
+ return -1;
+ priv->pending--;
+ priv->domain->p[evtchn].pending--;
+ return evtchn;
+}
+
+static int qemu_unmask(int handle, evtchn_port_t port)
+{
+ struct evtpriv *priv = getpriv(handle);
+ struct port *p;
+
+ if (!priv)
+ return -1;
+ if (port >= NR_EVENT_CHANNELS)
+ return -1;
+ p = priv->domain->p + port;
+ unmask_port(p);
+ return 0;
+}
+
+static int qemu_domid(int handle, int domid)
+{
+ struct evtpriv *priv = getpriv(handle);
+
+ if (!priv)
+ return -1;
+ if (priv->ports)
+ return -1;
+ priv->domain = get_domain(domid);
+ return 0;
+}
+
+struct XenEvtOps xc_evtchn_xenner = {
+ .open = qemu_open,
+ .domid = qemu_domid,
+ .close = qemu_close,
+ .fd = qemu_fd,
+ .notify = qemu_notify,
+ .bind_unbound_port = qemu_bind_unbound_port,
+ .bind_interdomain = qemu_bind_interdomain,
+ .bind_virq = qemu_bind_virq,
+ .unbind = qemu_unbind,
+ .pending = qemu_pending,
+ .unmask = qemu_unmask,
+};
+
+/* ------------------------------------------------------------- */
+
+void do_info_evtchn(Monitor *mon)
+{
+ struct evtpriv *priv;
+ struct port *port;
+ int i;
+
+ if (xen_mode != XEN_EMULATE) {
+ monitor_printf(mon, "Not emulating xen event channels.\n");
+ return;
+ }
+
+ TAILQ_FOREACH(priv, &privs, list) {
+ monitor_printf(mon, "%p: domid %d, fds %d,%d\n", priv,
+ priv->domain->domid,
+ priv->fd_read, priv->fd_write);
+ for (i = 1; i < NR_EVENT_CHANNELS; i++) {
+ port = priv->domain->p + i;
+ if (port->priv != priv)
+ continue;
+ monitor_printf(mon, " port #%d: ", port->port);
+ if (port->peer)
+ monitor_printf(mon, "peer #%d (%p, domid %d)\n",
+ port->peer->port, port->peer->priv,
+ port->peer->priv->domain->domid);
+ else
+ monitor_printf(mon, "no peer\n");
+ }
+ }
+}
diff --git a/monitor.c b/monitor.c
index b33fea1..33b0dcc 100644
--- a/monitor.c
+++ b/monitor.c
@@ -27,6 +27,7 @@
#include "hw/pcmcia.h"
#include "hw/pc.h"
#include "hw/pci.h"
+#include "hw/xen.h"
#include "gdbstub.h"
#include "net.h"
#include "qemu-char.h"
@@ -1846,6 +1847,10 @@ static const mon_cmd_t info_cmds[] = {
{ "migrate", "", do_info_migrate, "", "show migration status" },
{ "balloon", "", do_info_balloon,
"", "show balloon information" },
+#if defined(CONFIG_XENNER)
+ { "evtchn", "", do_info_evtchn,
+ "", "show xenner event channel information" },
+#endif
{ NULL, NULL, },
};
--
1.6.2.2