/* * The Qubes OS Project, http://www.qubes-os.org * * Copyright (C) 2010 Rafal Wojtczuk * * 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; either version 2 * of the License, or (at your option) any later version. * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include "libvchan.h" #include "gntalloc.h" static int ring_init(struct libvchan *ctrl) { ctrl->ring_fd = open("/dev/xen/gntalloc", O_RDWR); if (ctrl->ring_fd < 0) return -1; struct ioctl_gntalloc_alloc_gref gref_info = { .owner_domid = 0, // currently UNUSED in linux .foreign_domid = ctrl->other_domain_id, .readonly = 0 }; int err = ioctl(ctrl->ring_fd, IOCTL_GNTALLOC_ALLOC_GREF, &gref_info); if (err) return -1; ctrl->ring_ref = gref_info.gref_id; void* ring = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, ctrl->ring_fd, gref_info.page_idx << PAGE_SHIFT); if (ring == MAP_FAILED) return -1; ctrl->ring = ring; memset(ring, 0, 4096); ctrl->ring->debug = 0xabcdef01; return 0; } /** creates event channel; creates "ring-ref" and "event-channel" xenstore entries; waits for connection to event channel from the peer */ static int server_interface_init(struct libvchan *ctrl) { int ret = -1; struct xs_handle *xs; char buf[64]; char ref[16]; int event_fd; evtchn_port_or_error_t port; xs = xs_domain_open(); if (!xs) goto fail; ctrl->event_fd = xc_evtchn_open(); if (ctrl->event_fd < 0) goto fail_xs_open; port = xc_evtchn_bind_unbound_port(ctrl->event_fd, ctrl->other_domain_id); if (port < 0) goto fail_evt_open; ctrl->event_port = port; snprintf(ref, sizeof ref, "%d", ctrl->ring_ref); snprintf(buf, sizeof buf, "device/vchan/%d/ring-ref", ctrl->device_number); if (!xs_write(xs, 0, buf, ref, strlen(ref))) goto fail_evt_open; snprintf(ref, sizeof ref, "%d", ctrl->event_port); snprintf(buf, sizeof buf, "device/vchan/%d/event-channel", ctrl->device_number); if (!xs_write(xs, 0, buf, ref, strlen(ref))) goto fail_evt_open; printf("Set up server interface on vchan/%d: ref=%d channel=%d\n", ctrl->device_number, ctrl->ring_ref, ctrl->event_port); // // wait for the peer to arrive // if (xc_evtchn_pending(ctrl->event_fd) == -1) // goto fail_evt_open; // printf("Got peer connection\n"); xc_evtchn_unmask(ctrl->event_fd, ctrl->event_port); // snprintf(buf, sizeof buf, "device/vchan/%d", ctrl->device_number); // xs_rm(xs, 0, buf); ret = 0; fail_evt_open: if (ret) { close(ctrl->event_fd); ctrl->event_fd = -1; } fail_xs_open: xs_daemon_close(xs); fail: return ret; } /** Run in AppVM (any domain). Sleeps until the connection is established. \param devno something like a well-known port. \returns NULL on failure, handle on success */ struct libvchan *libvchan_server_init(int domain, int devno) { struct libvchan *ctrl = malloc(sizeof(struct libvchan)); if (!ctrl) return 0; ctrl->other_domain_id = domain; ctrl->device_number = devno; ctrl->ring = NULL; ctrl->ring_fd = ctrl->event_fd = -1; ctrl->is_server = 1; if (ring_init(ctrl)) return 0; if (server_interface_init(ctrl)) return 0; return ctrl; } /** retrieves ring-ref and event-channel numbers from xenstore (if they don't exist, return error, because nobody seems to listen); map the ring, connect the event channel */ static int client_interface_init(struct libvchan *ctrl) { int ret = -1; unsigned int len; struct xs_handle *xs; xc_interface* xc_handle; char buf[64]; char *ref; int event_fd; int remote_port; xs = xs_daemon_open(); if (!xs) xs = xs_domain_open(); if (!xs) goto out; snprintf(buf, sizeof buf, "/local/domain/%d/device/vchan/%d/ring-ref", ctrl->other_domain_id, ctrl->device_number); ref = xs_read(xs, 0, buf, &len); if (!ref) goto out_xs_open; ctrl->ring_ref = atoi(ref); free(ref); if (!ctrl->ring_ref) goto out_xs_open; snprintf(buf, sizeof buf, "/local/domain/%d/device/vchan/%d/event-channel", ctrl->other_domain_id, ctrl->device_number); ref = xs_read(xs, 0, buf, &len); if (!ref) goto out_xs_open; remote_port = atoi(ref); free(ref); if (!remote_port) goto out_xs_open; ctrl->ring_fd = xc_gnttab_open(NULL); if (ctrl->ring_fd < 0) goto out_xs_open; ctrl->ring = xc_gnttab_map_grant_ref(NULL, ctrl->ring_fd, ctrl->other_domain_id, ctrl->ring_ref, PROT_READ | PROT_WRITE); if (ctrl->ring == 0 || ctrl->ring == MAP_FAILED) goto out_gt_open; ctrl->event_fd = xc_evtchn_open(); if (ctrl->event_fd < 0) goto out_gt_open; ctrl->event_port = xc_evtchn_bind_interdomain(ctrl->event_fd, ctrl->other_domain_id, remote_port); if (ctrl->event_port < 0) goto out_ec_open; printf("Set up client interface on vchan/%d: ref=%d channel=%d debug=%x\n", ctrl->device_number, ctrl->ring_ref, ctrl->event_port, ctrl->ring->debug); ctrl->ring->debug = 0xabcdef02; xc_evtchn_unmask(ctrl->event_fd, ctrl->event_port); printf("Waiting for initial event:\n"); if (xc_evtchn_pending(ctrl->event_fd) == -1) goto out_ec_open; xc_evtchn_unmask(ctrl->event_fd, ctrl->event_port); printf("Client interface ready; debug=%x\n", ctrl->ring->debug); ctrl->ring->debug = 0xabcdef03; ret = 0; goto out_xs_open; out_ec_open: xc_evtchn_close(ctrl->event_fd); ctrl->event_fd = -1; out_gt_open: xc_gnttab_close(NULL, ctrl->ring_fd); ctrl->ring_fd = -1; out_xs_open: xs_daemon_close(xs); out: return ret; } /** Run on the client side of connection (currently, must be dom0). \returns NULL on failure (e.g. noone listening), handle on success */ struct libvchan *libvchan_client_init(int domain, int devno) { struct libvchan *ctrl = malloc(sizeof(struct libvchan)); if (!ctrl) return 0; ctrl->other_domain_id = domain; ctrl->device_number = devno; ctrl->ring = NULL; ctrl->ring_fd = ctrl->event_fd = -1; ctrl->is_server = 0; if (client_interface_init(ctrl)) return 0; return ctrl; }