>From a0a4cfe5040d3792875d7cd105aa2b832f6d8967 Mon Sep 17 00:00:00 2001
From: Gerd Hoffmann
Date: Wed, 22 Apr 2009 12:56:04 +0200
Subject: [PATCH 5/5] xenner: event channel implementation.
Signed-off-by: Gerd Hoffmann
---
Makefile.target | 6 +
configure | 13 ++-
hw/xen.h | 2 +
hw/xen_backend.h | 1 +
hw/xenner_hooks.h | 21 ++
hw/xenner_interfaces.h | 28 +++
hw/xenner_libxc_evtchn.c | 455 ++++++++++++++++++++++++++++++++++++++++++++++
monitor.c | 5 +
8 files changed, 530 insertions(+), 1 deletions(-)
create mode 100644 hw/xenner_hooks.h
create mode 100644 hw/xenner_interfaces.h
create mode 100644 hw/xenner_libxc_evtchn.c
diff --git a/Makefile.target b/Makefile.target
index 2171587..e630d21 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -568,6 +568,12 @@ ifeq ($(CONFIG_XEN), yes)
LIBS += $(XEN_LIBS)
endif
+# xenner support
+XENNER_OBJS := xenner_libxc_evtchn.o
+ifeq ($(CONFIG_XENNER), yes)
+ OBJS += $(XENNER_OBJS)
+endif
+
# SCSI layer
OBJS+= lsi53c895a.o esp.o
diff --git a/configure b/configure
index b7b7b01..9eb8221 100755
--- a/configure
+++ b/configure
@@ -192,6 +192,7 @@ blobs="yes"
fdt="yes"
sdl_x11="no"
xen="yes"
+xenner="yes"
pkgversion=""
# OS specific
@@ -422,7 +423,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"
;;
@@ -590,6 +593,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"
@@ -818,6 +822,7 @@ EOF
:
else
xen="no"
+ xenner="no"
fi
fi
@@ -1317,6 +1322,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" ] && \
@@ -1806,6 +1812,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..e9863cd 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 "xenner_hooks.h"
#include "sysemu.h"
#include "net.h"
#include "block_int.h"
diff --git a/hw/xenner_hooks.h b/hw/xenner_hooks.h
new file mode 100644
index 0000000..168a474
--- /dev/null
+++ b/hw/xenner_hooks.h
@@ -0,0 +1,21 @@
+#ifndef QEMU_HW_XENNER_HOOKS_H
+#define QEMU_HW_XENNER_HOOKS_H 1
+
+#include "xenner_interfaces.h"
+
+#if defined(CONFIG_XENNER)
+
+/* 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 /* CONFIG_XENNER */
+#endif /* QEMU_HW_XENNER_HOOKS_H */
diff --git a/hw/xenner_interfaces.h b/hw/xenner_interfaces.h
new file mode 100644
index 0000000..2a96ff4
--- /dev/null
+++ b/hw/xenner_interfaces.h
@@ -0,0 +1,28 @@
+#ifndef QEMU_HW_XENNER_INTERFACES_H
+#define QEMU_HW_XENNER_INTERFACES_H 1
+
+#if defined(CONFIG_XENNER)
+
+/* ------------------------------------------------------------- */
+/* 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;
+
+void xenner_evtchn_init(void);
+
+#endif /* CONFIG_XENNER */
+#endif /* QEMU_HW_XENNER_INTERFACES_H */
diff --git a/hw/xenner_libxc_evtchn.c b/hw/xenner_libxc_evtchn.c
new file mode 100644
index 0000000..1fc49f3
--- /dev/null
+++ b/hw/xenner_libxc_evtchn.c
@@ -0,0 +1,455 @@
+#include
+
+#include "hw.h"
+#include "qemu-log.h"
+#include "console.h"
+#include "monitor.h"
+#include "xen.h"
+#include "xenner_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;
+ int refcount;
+ struct port p[NR_EVENT_CHANNELS];
+ TAILQ_ENTRY(domain) list;
+};
+static TAILQ_HEAD(domain_head, domain) domains = TAILQ_HEAD_INITIALIZER(domains);
+
+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)
+{
+ struct domain *domain;
+
+ TAILQ_FOREACH(domain, &domains, list) {
+ if (domain->domid == domid)
+ goto done;
+ }
+
+ domain = qemu_mallocz(sizeof(*domain));
+ if (domid)
+ domain->domid = domid;
+ TAILQ_INSERT_TAIL(&domains, domain, list);
+ if (debug)
+ qemu_log("xen ev: ?: new domain id %d\n", domain->domid);
+
+done:
+ domain->refcount++;
+ return domain;
+}
+
+static void put_domain(struct domain *domain)
+{
+ domain->refcount--;
+ if (domain->refcount)
+ return;
+ if (debug)
+ qemu_log("xen ev: ?: del domain id %d\n", domain->domid);
+ TAILQ_REMOVE(&domains, domain, list);
+ qemu_free(domain);
+}
+
+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);
+ put_domain(domain);
+}
+
+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);
+ }
+ put_domain(priv->domain);
+
+ 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;
+ put_domain(priv->domain);
+ priv->domain = get_domain(domid);
+ return 0;
+}
+
+static struct XenEvtOps xenner_evtchn = {
+ .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,
+};
+
+/* ------------------------------------------------------------- */
+
+static int xc_evtchn_domid(int handle, int domid)
+{
+ return -1;
+}
+
+struct XenEvtOps xc_evtchn = {
+ .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,
+};
+
+/* ------------------------------------------------------------- */
+
+static int evtchn_emulation;
+
+void do_info_evtchn(Monitor *mon)
+{
+ struct evtpriv *priv;
+ struct port *port;
+ int i;
+
+ if (!evtchn_emulation) {
+ 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");
+ }
+ }
+}
+
+void xenner_evtchn_init(void)
+{
+ xc_evtchn = xenner_evtchn;
+ evtchn_emulation = 1;
+}
diff --git a/monitor.c b/monitor.c
index 3e945db..dc7ce7a 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