| /* |
| * Xen para-virtualization device |
| * |
| * (c) 2008 Gerd Hoffmann <kraxel@redhat.com> |
| * |
| * 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 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 <http://www.gnu.org/licenses/> |
| */ |
| |
| #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). |
| */ |
| void xen_pv_printf(struct XenLegacyDevice *xendev, int msg_level, |
| const char *fmt, ...) |
| { |
| va_list args; |
| |
| if (xendev) { |
| if (msg_level > xendev->debug) { |
| return; |
| } |
| qemu_log("xen be: %s: ", xendev->name); |
| if (msg_level == 0) { |
| fprintf(stderr, "xen be: %s: ", xendev->name); |
| } |
| } else { |
| if (msg_level > debug) { |
| return; |
| } |
| qemu_log("xen be core: "); |
| if (msg_level == 0) { |
| fprintf(stderr, "xen be core: "); |
| } |
| } |
| va_start(args, fmt); |
| qemu_log_vprintf(fmt, args); |
| va_end(args); |
| if (msg_level == 0) { |
| va_start(args, fmt); |
| vfprintf(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); |
| } |