/* * Xen para-virtualization device * * (c) 2008 Gerd Hoffmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see */ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "hw/qdev-core.h" #include "hw/xen/xen-legacy-backend.h" #include "hw/xen/xen_pvdev.h" /* private */ static int debug; struct xs_dirs { char *xs_dir; QTAILQ_ENTRY(xs_dirs) list; }; static QTAILQ_HEAD(, xs_dirs) xs_cleanup = QTAILQ_HEAD_INITIALIZER(xs_cleanup); static QTAILQ_HEAD(, XenLegacyDevice) xendevs = QTAILQ_HEAD_INITIALIZER(xendevs); /* ------------------------------------------------------------- */ static void xenstore_cleanup_dir(char *dir) { struct xs_dirs *d; d = g_malloc(sizeof(*d)); d->xs_dir = dir; QTAILQ_INSERT_TAIL(&xs_cleanup, d, list); } void xen_config_cleanup(void) { struct xs_dirs *d; QTAILQ_FOREACH(d, &xs_cleanup, list) { xs_rm(xenstore, 0, d->xs_dir); } } int xenstore_mkdir(char *path, int p) { struct xs_permissions perms[2] = { { .id = 0, /* set owner: dom0 */ }, { .id = xen_domid, .perms = p, } }; if (!xs_mkdir(xenstore, 0, path)) { xen_pv_printf(NULL, 0, "xs_mkdir %s: failed\n", path); return -1; } xenstore_cleanup_dir(g_strdup(path)); if (!xs_set_permissions(xenstore, 0, path, perms, 2)) { xen_pv_printf(NULL, 0, "xs_set_permissions %s: failed\n", path); return -1; } return 0; } int xenstore_write_str(const char *base, const char *node, const char *val) { char abspath[XEN_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[XEN_BUFSIZE]; unsigned int len; char *str, *ret = NULL; snprintf(abspath, sizeof(abspath), "%s/%s", base, node); str = xs_read(xenstore, 0, abspath, &len); if (str != NULL) { /* move to qemu-allocated memory to make sure * callers can savely g_free() stuff. */ ret = g_strdup(str); free(str); } return ret; } int xenstore_write_int(const char *base, const char *node, int ival) { char val[12]; snprintf(val, sizeof(val), "%d", ival); return xenstore_write_str(base, node, val); } int xenstore_write_int64(const char *base, const char *node, int64_t ival) { char val[21]; snprintf(val, sizeof(val), "%"PRId64, 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; } g_free(val); return rc; } int xenstore_read_uint64(const char *base, const char *node, uint64_t *uval) { char *val; int rc = -1; val = xenstore_read_str(base, node); if (val && 1 == sscanf(val, "%"SCNu64, uval)) { rc = 0; } g_free(val); return rc; } void xenstore_update(void *unused) { char **vec = NULL; intptr_t type, ops, ptr; unsigned int dom, count; vec = xs_read_watch(xenstore, &count); if (vec == NULL) { goto cleanup; } if (sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR, &type, &dom, &ops) == 3) { xenstore_update_be(vec[XS_WATCH_PATH], (void *)type, dom, (void*)ops); } if (sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr) == 1) { xenstore_update_fe(vec[XS_WATCH_PATH], (void *)ptr); } cleanup: free(vec); } 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"; } /* * msg_level: * 0 == errors (stderr + logfile). * 1 == informative debug messages (logfile only). * 2 == noisy debug messages (logfile only). * 3 == will flood your log (logfile only). */ static void xen_pv_output_msg(struct XenLegacyDevice *xendev, FILE *f, const char *fmt, va_list args) { if (xendev) { fprintf(f, "xen be: %s: ", xendev->name); } else { fprintf(f, "xen be core: "); } vfprintf(f, fmt, args); } void xen_pv_printf(struct XenLegacyDevice *xendev, int msg_level, const char *fmt, ...) { FILE *logfile; va_list args; if (msg_level > (xendev ? xendev->debug : debug)) { return; } logfile = qemu_log_trylock(); if (logfile) { va_start(args, fmt); xen_pv_output_msg(xendev, logfile, fmt, args); va_end(args); qemu_log_unlock(logfile); } if (msg_level == 0) { va_start(args, fmt); xen_pv_output_msg(xendev, stderr, fmt, args); va_end(args); } qemu_log_flush(); } void xen_pv_evtchn_event(void *opaque) { struct XenLegacyDevice *xendev = opaque; evtchn_port_t port; port = xenevtchn_pending(xendev->evtchndev); if (port != xendev->local_port) { xen_pv_printf(xendev, 0, "xenevtchn_pending returned %d (expected %d)\n", port, xendev->local_port); return; } xenevtchn_unmask(xendev->evtchndev, port); if (xendev->ops->event) { xendev->ops->event(xendev); } } void xen_pv_unbind_evtchn(struct XenLegacyDevice *xendev) { if (xendev->local_port == -1) { return; } qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev), NULL, NULL, NULL); xenevtchn_unbind(xendev->evtchndev, xendev->local_port); xen_pv_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port); xendev->local_port = -1; } int xen_pv_send_notify(struct XenLegacyDevice *xendev) { return xenevtchn_notify(xendev->evtchndev, xendev->local_port); } /* ------------------------------------------------------------- */ struct XenLegacyDevice *xen_pv_find_xendev(const char *type, int dom, int dev) { struct XenLegacyDevice *xendev; QTAILQ_FOREACH(xendev, &xendevs, next) { if (xendev->dom != dom) { continue; } if (xendev->dev != dev) { continue; } if (strcmp(xendev->type, type) != 0) { continue; } return xendev; } return NULL; } /* * release xen backend device. */ void xen_pv_del_xendev(struct XenLegacyDevice *xendev) { if (xendev->ops->free) { xendev->ops->free(xendev); } if (xendev->fe) { char token[XEN_BUFSIZE]; snprintf(token, sizeof(token), "fe:%p", xendev); xs_unwatch(xenstore, xendev->fe, token); g_free(xendev->fe); } if (xendev->evtchndev != NULL) { xenevtchn_close(xendev->evtchndev); } if (xendev->gnttabdev != NULL) { xengnttab_close(xendev->gnttabdev); } QTAILQ_REMOVE(&xendevs, xendev, next); qdev_unplug(&xendev->qdev, NULL); } void xen_pv_insert_xendev(struct XenLegacyDevice *xendev) { QTAILQ_INSERT_TAIL(&xendevs, xendev, next); }