|  | /* | 
|  | * QEMU Xen backend support: Operations for true Xen | 
|  | * | 
|  | * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. | 
|  | * | 
|  | * Authors: David Woodhouse <dwmw2@infradead.org> | 
|  | * | 
|  | * This work is licensed under the terms of the GNU GPL, version 2 or later. | 
|  | * See the COPYING file in the top-level directory. | 
|  | */ | 
|  |  | 
|  | #include "qemu/osdep.h" | 
|  | #include "qemu/uuid.h" | 
|  | #include "qapi/error.h" | 
|  |  | 
|  | #include "hw/xen/xen_native.h" | 
|  | #include "hw/xen/xen_backend_ops.h" | 
|  |  | 
|  | /* | 
|  | * If we have new enough libxenctrl then we do not want/need these compat | 
|  | * interfaces, despite what the user supplied cflags might say. They | 
|  | * must be undefined before including xenctrl.h | 
|  | */ | 
|  | #undef XC_WANT_COMPAT_EVTCHN_API | 
|  | #undef XC_WANT_COMPAT_GNTTAB_API | 
|  | #undef XC_WANT_COMPAT_MAP_FOREIGN_API | 
|  |  | 
|  | #include <xenctrl.h> | 
|  |  | 
|  | /* | 
|  | * We don't support Xen prior to 4.7.1. | 
|  | */ | 
|  |  | 
|  | #include <xenevtchn.h> | 
|  | #include <xengnttab.h> | 
|  | #include <xenforeignmemory.h> | 
|  |  | 
|  | /* Xen before 4.8 */ | 
|  |  | 
|  | static int libxengnttab_fallback_grant_copy(xengnttab_handle *xgt, | 
|  | bool to_domain, uint32_t domid, | 
|  | XenGrantCopySegment segs[], | 
|  | unsigned int nr_segs, Error **errp) | 
|  | { | 
|  | uint32_t *refs = g_new(uint32_t, nr_segs); | 
|  | int prot = to_domain ? PROT_WRITE : PROT_READ; | 
|  | void *map; | 
|  | unsigned int i; | 
|  | int rc = 0; | 
|  |  | 
|  | for (i = 0; i < nr_segs; i++) { | 
|  | XenGrantCopySegment *seg = &segs[i]; | 
|  |  | 
|  | refs[i] = to_domain ? seg->dest.foreign.ref : | 
|  | seg->source.foreign.ref; | 
|  | } | 
|  | map = xengnttab_map_domain_grant_refs(xgt, nr_segs, domid, refs, prot); | 
|  | if (!map) { | 
|  | if (errp) { | 
|  | error_setg_errno(errp, errno, | 
|  | "xengnttab_map_domain_grant_refs failed"); | 
|  | } | 
|  | rc = -errno; | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < nr_segs; i++) { | 
|  | XenGrantCopySegment *seg = &segs[i]; | 
|  | void *page = map + (i * XEN_PAGE_SIZE); | 
|  |  | 
|  | if (to_domain) { | 
|  | memcpy(page + seg->dest.foreign.offset, seg->source.virt, | 
|  | seg->len); | 
|  | } else { | 
|  | memcpy(seg->dest.virt, page + seg->source.foreign.offset, | 
|  | seg->len); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (xengnttab_unmap(xgt, map, nr_segs)) { | 
|  | if (errp) { | 
|  | error_setg_errno(errp, errno, "xengnttab_unmap failed"); | 
|  | } | 
|  | rc = -errno; | 
|  | } | 
|  |  | 
|  | done: | 
|  | g_free(refs); | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | #if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40800 | 
|  |  | 
|  | static int libxengnttab_backend_grant_copy(xengnttab_handle *xgt, | 
|  | bool to_domain, uint32_t domid, | 
|  | XenGrantCopySegment *segs, | 
|  | uint32_t nr_segs, Error **errp) | 
|  | { | 
|  | xengnttab_grant_copy_segment_t *xengnttab_segs; | 
|  | unsigned int i; | 
|  | int rc; | 
|  |  | 
|  | xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs); | 
|  |  | 
|  | for (i = 0; i < nr_segs; i++) { | 
|  | XenGrantCopySegment *seg = &segs[i]; | 
|  | xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; | 
|  |  | 
|  | if (to_domain) { | 
|  | xengnttab_seg->flags = GNTCOPY_dest_gref; | 
|  | xengnttab_seg->dest.foreign.domid = domid; | 
|  | xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref; | 
|  | xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset; | 
|  | xengnttab_seg->source.virt = seg->source.virt; | 
|  | } else { | 
|  | xengnttab_seg->flags = GNTCOPY_source_gref; | 
|  | xengnttab_seg->source.foreign.domid = domid; | 
|  | xengnttab_seg->source.foreign.ref = seg->source.foreign.ref; | 
|  | xengnttab_seg->source.foreign.offset = | 
|  | seg->source.foreign.offset; | 
|  | xengnttab_seg->dest.virt = seg->dest.virt; | 
|  | } | 
|  |  | 
|  | xengnttab_seg->len = seg->len; | 
|  | } | 
|  |  | 
|  | if (xengnttab_grant_copy(xgt, nr_segs, xengnttab_segs)) { | 
|  | if (errp) { | 
|  | error_setg_errno(errp, errno, "xengnttab_grant_copy failed"); | 
|  | } | 
|  | rc = -errno; | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | rc = 0; | 
|  | for (i = 0; i < nr_segs; i++) { | 
|  | xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; | 
|  |  | 
|  | if (xengnttab_seg->status != GNTST_okay) { | 
|  | if (errp) { | 
|  | error_setg(errp, "xengnttab_grant_copy seg[%u] failed", i); | 
|  | } | 
|  | rc = -EIO; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | done: | 
|  | g_free(xengnttab_segs); | 
|  | return rc; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static xenevtchn_handle *libxenevtchn_backend_open(void) | 
|  | { | 
|  | return xenevtchn_open(NULL, 0); | 
|  | } | 
|  |  | 
|  | struct evtchn_backend_ops libxenevtchn_backend_ops = { | 
|  | .open = libxenevtchn_backend_open, | 
|  | .close = xenevtchn_close, | 
|  | .bind_interdomain = xenevtchn_bind_interdomain, | 
|  | .unbind = xenevtchn_unbind, | 
|  | .get_fd = xenevtchn_fd, | 
|  | .notify = xenevtchn_notify, | 
|  | .unmask = xenevtchn_unmask, | 
|  | .pending = xenevtchn_pending, | 
|  | }; | 
|  |  | 
|  | static xengnttab_handle *libxengnttab_backend_open(void) | 
|  | { | 
|  | return xengnttab_open(NULL, 0); | 
|  | } | 
|  |  | 
|  | static int libxengnttab_backend_unmap(xengnttab_handle *xgt, | 
|  | void *start_address, uint32_t *refs, | 
|  | uint32_t count) | 
|  | { | 
|  | return xengnttab_unmap(xgt, start_address, count); | 
|  | } | 
|  |  | 
|  |  | 
|  | static struct gnttab_backend_ops libxengnttab_backend_ops = { | 
|  | .features = XEN_GNTTAB_OP_FEATURE_MAP_MULTIPLE, | 
|  | .open = libxengnttab_backend_open, | 
|  | .close = xengnttab_close, | 
|  | .grant_copy = libxengnttab_fallback_grant_copy, | 
|  | .set_max_grants = xengnttab_set_max_grants, | 
|  | .map_refs = xengnttab_map_domain_grant_refs, | 
|  | .unmap = libxengnttab_backend_unmap, | 
|  | }; | 
|  |  | 
|  | static void *libxenforeignmem_backend_map(uint32_t dom, void *addr, int prot, | 
|  | size_t pages, xen_pfn_t *pfns, | 
|  | int *errs) | 
|  | { | 
|  | return xenforeignmemory_map2(xen_fmem, dom, addr, prot, 0, pages, pfns, | 
|  | errs); | 
|  | } | 
|  |  | 
|  | static int libxenforeignmem_backend_unmap(void *addr, size_t pages) | 
|  | { | 
|  | return xenforeignmemory_unmap(xen_fmem, addr, pages); | 
|  | } | 
|  |  | 
|  | struct foreignmem_backend_ops libxenforeignmem_backend_ops = { | 
|  | .map = libxenforeignmem_backend_map, | 
|  | .unmap = libxenforeignmem_backend_unmap, | 
|  | }; | 
|  |  | 
|  | struct qemu_xs_handle { | 
|  | struct xs_handle *xsh; | 
|  | NotifierList notifiers; | 
|  | }; | 
|  |  | 
|  | static void watch_event(void *opaque) | 
|  | { | 
|  | struct qemu_xs_handle *h = opaque; | 
|  |  | 
|  | for (;;) { | 
|  | char **v = xs_check_watch(h->xsh); | 
|  |  | 
|  | if (!v) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | notifier_list_notify(&h->notifiers, v); | 
|  | free(v); | 
|  | } | 
|  | } | 
|  |  | 
|  | static struct qemu_xs_handle *libxenstore_open(void) | 
|  | { | 
|  | struct xs_handle *xsh = xs_open(0); | 
|  | struct qemu_xs_handle *h; | 
|  |  | 
|  | if (!xsh) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | h = g_new0(struct qemu_xs_handle, 1); | 
|  | h->xsh = xsh; | 
|  |  | 
|  | notifier_list_init(&h->notifiers); | 
|  | qemu_set_fd_handler(xs_fileno(h->xsh), watch_event, NULL, h); | 
|  |  | 
|  | return h; | 
|  | } | 
|  |  | 
|  | static void libxenstore_close(struct qemu_xs_handle *h) | 
|  | { | 
|  | g_assert(notifier_list_empty(&h->notifiers)); | 
|  | qemu_set_fd_handler(xs_fileno(h->xsh), NULL, NULL, NULL); | 
|  | xs_close(h->xsh); | 
|  | g_free(h); | 
|  | } | 
|  |  | 
|  | static char *libxenstore_get_domain_path(struct qemu_xs_handle *h, | 
|  | unsigned int domid) | 
|  | { | 
|  | return xs_get_domain_path(h->xsh, domid); | 
|  | } | 
|  |  | 
|  | static char **libxenstore_directory(struct qemu_xs_handle *h, | 
|  | xs_transaction_t t, const char *path, | 
|  | unsigned int *num) | 
|  | { | 
|  | return xs_directory(h->xsh, t, path, num); | 
|  | } | 
|  |  | 
|  | static void *libxenstore_read(struct qemu_xs_handle *h, xs_transaction_t t, | 
|  | const char *path, unsigned int *len) | 
|  | { | 
|  | return xs_read(h->xsh, t, path, len); | 
|  | } | 
|  |  | 
|  | static bool libxenstore_write(struct qemu_xs_handle *h, xs_transaction_t t, | 
|  | const char *path, const void *data, | 
|  | unsigned int len) | 
|  | { | 
|  | return xs_write(h->xsh, t, path, data, len); | 
|  | } | 
|  |  | 
|  | static bool libxenstore_create(struct qemu_xs_handle *h, xs_transaction_t t, | 
|  | unsigned int owner, unsigned int domid, | 
|  | unsigned int perms, const char *path) | 
|  | { | 
|  | struct xs_permissions perms_list[] = { | 
|  | { | 
|  | .id    = owner, | 
|  | .perms = XS_PERM_NONE, | 
|  | }, | 
|  | { | 
|  | .id    = domid, | 
|  | .perms = perms, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | if (!xs_mkdir(h->xsh, t, path)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return xs_set_permissions(h->xsh, t, path, perms_list, | 
|  | ARRAY_SIZE(perms_list)); | 
|  | } | 
|  |  | 
|  | static bool libxenstore_destroy(struct qemu_xs_handle *h, xs_transaction_t t, | 
|  | const char *path) | 
|  | { | 
|  | return xs_rm(h->xsh, t, path); | 
|  | } | 
|  |  | 
|  | struct qemu_xs_watch { | 
|  | char *path; | 
|  | char *token; | 
|  | xs_watch_fn fn; | 
|  | void *opaque; | 
|  | Notifier notifier; | 
|  | }; | 
|  |  | 
|  | static void watch_notify(Notifier *n, void *data) | 
|  | { | 
|  | struct qemu_xs_watch *w = container_of(n, struct qemu_xs_watch, notifier); | 
|  | const char **v = data; | 
|  |  | 
|  | if (!strcmp(w->token, v[XS_WATCH_TOKEN])) { | 
|  | w->fn(w->opaque, v[XS_WATCH_PATH]); | 
|  | } | 
|  | } | 
|  |  | 
|  | static struct qemu_xs_watch *new_watch(const char *path, xs_watch_fn fn, | 
|  | void *opaque) | 
|  | { | 
|  | struct qemu_xs_watch *w = g_new0(struct qemu_xs_watch, 1); | 
|  | QemuUUID uuid; | 
|  |  | 
|  | qemu_uuid_generate(&uuid); | 
|  |  | 
|  | w->token = qemu_uuid_unparse_strdup(&uuid); | 
|  | w->path = g_strdup(path); | 
|  | w->fn = fn; | 
|  | w->opaque = opaque; | 
|  | w->notifier.notify = watch_notify; | 
|  |  | 
|  | return w; | 
|  | } | 
|  |  | 
|  | static void free_watch(struct qemu_xs_watch *w) | 
|  | { | 
|  | g_free(w->token); | 
|  | g_free(w->path); | 
|  |  | 
|  | g_free(w); | 
|  | } | 
|  |  | 
|  | static struct qemu_xs_watch *libxenstore_watch(struct qemu_xs_handle *h, | 
|  | const char *path, xs_watch_fn fn, | 
|  | void *opaque) | 
|  | { | 
|  | struct qemu_xs_watch *w = new_watch(path, fn, opaque); | 
|  |  | 
|  | notifier_list_add(&h->notifiers, &w->notifier); | 
|  |  | 
|  | if (!xs_watch(h->xsh, path, w->token)) { | 
|  | notifier_remove(&w->notifier); | 
|  | free_watch(w); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | return w; | 
|  | } | 
|  |  | 
|  | static void libxenstore_unwatch(struct qemu_xs_handle *h, | 
|  | struct qemu_xs_watch *w) | 
|  | { | 
|  | xs_unwatch(h->xsh, w->path, w->token); | 
|  | notifier_remove(&w->notifier); | 
|  | free_watch(w); | 
|  | } | 
|  |  | 
|  | static xs_transaction_t libxenstore_transaction_start(struct qemu_xs_handle *h) | 
|  | { | 
|  | return xs_transaction_start(h->xsh); | 
|  | } | 
|  |  | 
|  | static bool libxenstore_transaction_end(struct qemu_xs_handle *h, | 
|  | xs_transaction_t t, bool abort) | 
|  | { | 
|  | return xs_transaction_end(h->xsh, t, abort); | 
|  | } | 
|  |  | 
|  | struct xenstore_backend_ops libxenstore_backend_ops = { | 
|  | .open = libxenstore_open, | 
|  | .close = libxenstore_close, | 
|  | .get_domain_path = libxenstore_get_domain_path, | 
|  | .directory = libxenstore_directory, | 
|  | .read = libxenstore_read, | 
|  | .write = libxenstore_write, | 
|  | .create = libxenstore_create, | 
|  | .destroy = libxenstore_destroy, | 
|  | .watch = libxenstore_watch, | 
|  | .unwatch = libxenstore_unwatch, | 
|  | .transaction_start = libxenstore_transaction_start, | 
|  | .transaction_end = libxenstore_transaction_end, | 
|  | }; | 
|  |  | 
|  | void setup_xen_backend_ops(void) | 
|  | { | 
|  | #if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40800 | 
|  | xengnttab_handle *xgt = xengnttab_open(NULL, 0); | 
|  |  | 
|  | if (xgt) { | 
|  | if (xengnttab_grant_copy(xgt, 0, NULL) == 0) { | 
|  | libxengnttab_backend_ops.grant_copy = libxengnttab_backend_grant_copy; | 
|  | } | 
|  | xengnttab_close(xgt); | 
|  | } | 
|  | #endif | 
|  | xen_evtchn_ops = &libxenevtchn_backend_ops; | 
|  | xen_gnttab_ops = &libxengnttab_backend_ops; | 
|  | xen_foreignmem_ops = &libxenforeignmem_backend_ops; | 
|  | xen_xenstore_ops = &libxenstore_backend_ops; | 
|  | } |