---
hw/xenner_libxc_evtchn.c | 467 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 467 insertions(+), 0 deletions(-)
create mode 100644 hw/xenner_libxc_evtchn.c
diff --git a/hw/xenner_libxc_evtchn.c b/hw/xenner_libxc_evtchn.c
new file mode 100644
index 0000000..bb1984c
--- /dev/null
+++ b/hw/xenner_libxc_evtchn.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) Red Hat 2007
+ * Copyright (C) Novell Inc. 2010
+ *
+ * Author(s): Gerd Hoffmann<address@hidden>
+ * Alexander Graf<address@hidden>
+ *
+ * Xenner emulation -- event channels
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see<http://www.gnu.org/licenses/>.
+ */
+
+#include<assert.h>
+#include<xenctrl.h>
+
+#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;
+ QTAILQ_ENTRY(evtpriv) list;
+};
+static QTAILQ_HEAD(evtpriv_head, evtpriv) privs =
QTAILQ_HEAD_INITIALIZER(privs);
+
+static int debug = 0;
+
+/* ------------------------------------------------------------- */
+
+static struct evtpriv *getpriv(int handle)
+{
+ struct evtpriv *priv;
+
+ QTAILQ_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) {
+ 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++) {
+#ifdef DEBUG
+ /* debug hack */
+#define EA_START 20
+ if (priv->domain->domid&& i< EA_START)
+ i = EA_START;
+#undef 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;
+ int r;
+
+ peer->count_snd++;
+ if (peer->pending) {
+ return;
+ }
+
+ r = write(peer->priv->fd_write,&evtchn, sizeof(evtchn));
+ if (r != sizeof(evtchn)) {
+ // XXX break
+ }
+ 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) {
+ fprintf(stderr, "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_xopen(void)
+{
+ struct evtpriv *priv;
+ int fd[2];
+
+ priv = qemu_mallocz(sizeof(*priv));
+ QTAILQ_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);
+ QTAILQ_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_xopen,
+ .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,
+};
+
+/* ------------------------------------------------------------- */
+
+#if 0
+
+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;
+ }
+
+ QTAILQ_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");
+ }
+ }
+ }
+}
+
+#endif
+