This adds a xen event channel implementation to qemu, intented to be
used by xenner (aka xen emulation).
The patch also adds a XenEvtOps struct with function pointers for the
xc_evtchn_* family, which is used to switch between libxenctrl and the
qemu implementation at runtime. By default libxenctrl is used.
---
Makefile.target | 1 +
hw/xen_interfaces.h | 27 +++
hw/xen_machine_pv.c | 2 +
hw/xenner_libxc_evtchn.c | 396 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 426 insertions(+), 0 deletions(-)
create mode 100644 hw/xen_interfaces.h
create mode 100644 hw/xenner_libxc_evtchn.c
diff --git a/Makefile.target b/Makefile.target
index 5c97874..b88fd8f 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -521,6 +521,7 @@ 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 xen_framebuffer.o xen_disk.o xen_nic.o
+XEN_OBJS += xenner_libxc_evtchn.o
ifeq ($(CONFIG_XEN), yes)
OBJS += $(XEN_OBJS)
LIBS += $(XEN_LIBS)
diff --git a/hw/xen_interfaces.h b/hw/xen_interfaces.h
new file mode 100644
index 0000000..869b382
--- /dev/null
+++ b/hw/xen_interfaces.h
@@ -0,0 +1,27 @@
+#ifndef QEMU_XEN_INTERFACES_H
+#define QEMU_XEN_INTERFACES_H 1
+
+#include "xen_common.h"
+
+/* ------------------------------------------------------------- */
+/* 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 /* QEMU_XEN_INTERFACES_H */
diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c
index 9c67848..5d755f5 100644
--- a/hw/xen_machine_pv.c
+++ b/hw/xen_machine_pv.c
@@ -26,6 +26,7 @@
#include "boards.h"
#include "xen_backend.h"
+#include "xen_interfaces.h"
#include "xen_domainbuild.h"
/* -------------------------------------------------------------------- */
@@ -80,6 +81,7 @@ static int xen_init(void)
if (!xen_detect()) {
fprintf(stderr, "%s: emulating Xen\n", __FUNCTION__);
xen_emulate = 1;
+ xenner_evtchn_init();
}
if (-1 == xen_be_init()) {
diff --git a/hw/xenner_libxc_evtchn.c b/hw/xenner_libxc_evtchn.c
new file mode 100644
index 0000000..f0c179d
--- /dev/null
+++ b/hw/xenner_libxc_evtchn.c
@@ -0,0 +1,396 @@
+#include <xenctrl.h>
+
+#include "hw.h"
+#include "xen_interfaces.h"
+
+/* ------------------------------------------------------------- */
+
+struct evtpriv;
+
+struct port {
+ struct evtpriv *priv;
+ struct port *peer;
+ int port;
+};
+
+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;
+ TAILQ_ENTRY(evtpriv) list;
+};
+static TAILQ_HEAD(evtpriv_head, evtpriv) privs = TAILQ_HEAD_INITIALIZER(privs);
+
+static int debug = 0;
+
+/* ------------------------------------------------------------- */
+
+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 (NULL == domain)
+ return NULL;
+ if (domid)
+ domain->domid = domid;
+ TAILQ_INSERT_TAIL(&domains, domain, list);
+ if (debug)
+ fprintf(stderr, "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)
+ fprintf(stderr, "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, char *reason)
+{
+ struct port *p = NULL;
+ int i;
+
+ for (i = 1; i < NR_EVENT_CHANNELS; i++) {
+ if (NULL != priv->domain->p[i].priv)
+ continue;
+ p = priv->domain->p+i;
+ p->port = i;
+ p->priv = priv;
+ priv->ports++;
+ if (debug)
+ fprintf(stderr, "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;
+ 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)
+ fprintf(stderr, "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;
+ write(peer->priv->fd_write, &evtchn, sizeof(evtchn));
+}
+
+static void notify_port(struct port *p)
+{
+ if (p->peer) {
+ notify_send_peer(p->peer);
+ if (debug > 1)
+ fprintf(stderr, "xen ev:%3d: notify port %d domain %d -> port %d
domain %d\n",
+ p->priv->fd_read, p->port, p->priv->domain->domid,
+ p->peer->port, p->peer->priv->domain->domid);
+ } 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));
+ if (NULL == priv)
+ goto err;
+ TAILQ_INSERT_TAIL(&privs, priv, list);
+
+ if (-1 == pipe(fd))
+ 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;
+ /*
+ * FIXME: port not linked
+ * (but virq doesn't go this route anyway I think)
+ */
+ 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;
+ 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,
+};
+
+/* ------------------------------------------------------------- */
+
+void xenner_evtchn_init(void)
+{
+ xc_evtchn = xenner_evtchn;
+}
--
1.5.5.1
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|