From f263aa6b7c0d2cc34a55004eb0e05a568032c626 Mon Sep 17 00:00:00 2001 From: Rafal Wojtczuk Date: Tue, 8 Mar 2011 12:24:47 +0100 Subject: [PATCH] Moved vchan and u2mfn code to core. --- Makefile | 1 + qrexec/Makefile | 2 +- rpm_spec/core-appvm.spec | 26 +++++ rpm_spec/core-dom0.spec | 7 ++ rpm_spec/core-netvm.spec | 7 ++ u2mfn/Makefile | 33 ++++++ u2mfn/u2mfn-kernel.h | 26 +++++ u2mfn/u2mfnlib.c | 75 +++++++++++++ u2mfn/u2mfnlib.h | 24 +++++ vchan/Makefile | 39 +++++++ vchan/init.c | 224 +++++++++++++++++++++++++++++++++++++++ vchan/io.c | 159 +++++++++++++++++++++++++++ vchan/libvchan.h | 60 +++++++++++ vchan/node-select.c | 133 +++++++++++++++++++++++ vchan/node.c | 157 +++++++++++++++++++++++++++ 15 files changed, 972 insertions(+), 1 deletion(-) create mode 100644 u2mfn/Makefile create mode 100644 u2mfn/u2mfn-kernel.h create mode 100644 u2mfn/u2mfnlib.c create mode 100644 u2mfn/u2mfnlib.h create mode 100644 vchan/Makefile create mode 100644 vchan/init.c create mode 100644 vchan/io.c create mode 100644 vchan/libvchan.h create mode 100644 vchan/node-select.c create mode 100644 vchan/node.c diff --git a/Makefile b/Makefile index 5601dcd7..bab9705b 100644 --- a/Makefile +++ b/Makefile @@ -28,3 +28,4 @@ clean: (cd dom0/qmemman && make clean) (cd common && make clean) make -C qrexec clean + make -C vchan clean diff --git a/qrexec/Makefile b/qrexec/Makefile index 72089fda..b87b09d6 100644 --- a/qrexec/Makefile +++ b/qrexec/Makefile @@ -1,5 +1,5 @@ CC=gcc -CFLAGS+=-g -Wall +CFLAGS+=-g -Wall -I../vchan XENLIBS=-lvchan -lxenstore -lxenctrl all: qrexec_daemon qrexec_agent qrexec_client diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index 6dccb5fb..8ca5daee 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -61,6 +61,8 @@ fi make clean all make -C ../common make -C ../qrexec +make -C ../vchan +make -C ../u2mfn %install @@ -98,6 +100,14 @@ cp xorg-preload-apps.conf $RPM_BUILD_ROOT/etc/X11 mkdir -p $RPM_BUILD_ROOT/home_volatile/user chown 500:500 $RPM_BUILD_ROOT/home_volatile/user +install -D ../vchan/libvchan.h $RPM_BUILD_ROOT/usr/include/libvchan.h +install -D ../u2mfn/u2mfnlib.h $RPM_BUILD_ROOT/usr/include/u2mfnlib.h +install -D ../u2mfn/u2mfn-kernel.h $RPM_BUILD_ROOT/usr/include/u2mfn-kernel.h + +install -D ../vchan/libvchan.so $RPM_BUILD_ROOT/%{_libdir}/libvchan.so +install -D ../u2mfn/libu2mfn.so $RPM_BUILD_ROOT/%{_libdir}/libu2mfn.so + + %triggerin -- initscripts cp /var/lib/qubes/serial.conf /etc/init/serial.conf @@ -214,3 +224,19 @@ rm -rf $RPM_BUILD_ROOT %dir /home_volatile %attr(700,user,user) /home_volatile/user /etc/X11/xorg-preload-apps.conf +/usr/include/libvchan.h +%{_libdir}/libvchan.so +%{_libdir}/libu2mfn.so + + +%package devel +Summary: Include files for qubes core libraries +License: GPL v2 only +Group: Development/Sources + +%description devel + +%files devel +/usr/include/libvchan.h +/usr/include/u2mfnlib.h +/usr/include/u2mfn-kernel.h diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 11182506..90163e28 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -50,6 +50,8 @@ python -O -m compileall qvm-core qmemman make -C restore make -C ../common make -C ../qrexec +make -C ../vchan +make -C ../u2mfn %install @@ -125,6 +127,9 @@ cp pm-utils/02qubes-pause-vms $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d/ mkdir -p $RPM_BUILD_ROOT/var/log/qubes mkdir -p $RPM_BUILD_ROOT/var/run/qubes +install -D ../vchan/libvchan.so $RPM_BUILD_ROOT/%{_libdir}/libvchan.so +install -D ../u2mfn/libu2mfn.so $RPM_BUILD_ROOT/%{_libdir}/libu2mfn.so + %post /usr/lib/qubes/qubes_fix_nm_conf.sh @@ -284,3 +289,5 @@ fi %attr(4750,root,qubes) /usr/lib/qubes/xenfreepages %attr(2770,root,qubes) %dir /var/log/qubes %attr(770,root,qubes) %dir /var/run/qubes +%{_libdir}/libvchan.so +%{_libdir}/libu2mfn.so diff --git a/rpm_spec/core-netvm.spec b/rpm_spec/core-netvm.spec index 678da3a9..6c88fcac 100644 --- a/rpm_spec/core-netvm.spec +++ b/rpm_spec/core-netvm.spec @@ -51,6 +51,8 @@ fi %build make -C ../qrexec +make -C ../vchan +make -C ../u2mfn %install @@ -78,6 +80,9 @@ cp ../common/serial.conf $RPM_BUILD_ROOT/var/lib/qubes/ mkdir -p $RPM_BUILD_ROOT/var/run/qubes mkdir -p $RPM_BUILD_ROOT/etc/xen/scripts cp ../common/vif-route-qubes $RPM_BUILD_ROOT/etc/xen/scripts +install -D ../vchan/libvchan.so $RPM_BUILD_ROOT/%{_libdir}/libvchan.so +install -D ../u2mfn/libu2mfn.so $RPM_BUILD_ROOT/%{_libdir}/libu2mfn.so + %triggerin -- initscripts cp /var/lib/qubes/serial.conf /etc/init/serial.conf @@ -186,3 +191,5 @@ rm -rf $RPM_BUILD_ROOT /sbin/qubes_serial_login /etc/xen/scripts/vif-route-qubes %dir /var/run/qubes +%{_libdir}/libvchan.so +%{_libdir}/libu2mfn.so diff --git a/u2mfn/Makefile b/u2mfn/Makefile new file mode 100644 index 00000000..9f08dcce --- /dev/null +++ b/u2mfn/Makefile @@ -0,0 +1,33 @@ +# +# 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. +# +# + +CC=gcc +CFLAGS=-g -Wall +all: libu2mfn.so + +libu2mfn.so : u2mfnlib.o + gcc -shared -o libu2mfn.so u2mfnlib.o +u2mfnlib.o: u2mfnlib.c + gcc -fPIC -Wall -g -c u2mfnlib.c +clean: + rm -f *.o *so *~ libu2mfn.so + + diff --git a/u2mfn/u2mfn-kernel.h b/u2mfn/u2mfn-kernel.h new file mode 100644 index 00000000..ee244bc1 --- /dev/null +++ b/u2mfn/u2mfn-kernel.h @@ -0,0 +1,26 @@ +/* + * 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 + +#define U2MFN_MAGIC 0xf5 // See ioctl-number.txt in kernel docs + +#define U2MFN_GET_MFN_FOR_PAGE _IOW (U2MFN_MAGIC, 1, int) +#define U2MFN_GET_LAST_MFN _IO (U2MFN_MAGIC, 2) diff --git a/u2mfn/u2mfnlib.c b/u2mfn/u2mfnlib.c new file mode 100644 index 00000000..998f47e9 --- /dev/null +++ b/u2mfn/u2mfnlib.c @@ -0,0 +1,75 @@ +/* + * 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 "u2mfn-kernel.h" + + +static int u2mfn_fd = -1; + +static int get_fd() +{ + if (u2mfn_fd == -1) { + u2mfn_fd = open("/proc/u2mfn", O_RDWR); + if (u2mfn_fd < 0) + return -1; + } + return 0; +} + +int u2mfn_get_mfn_for_page(long va, int *mfn) +{ + if (get_fd()) + return -1; + *mfn = ioctl(u2mfn_fd, U2MFN_GET_MFN_FOR_PAGE, va); + if (*mfn == -1) + return -1; + + return 0; +} + +int u2mfn_get_last_mfn(int *mfn) +{ + if (get_fd()) + return -1; + + *mfn = ioctl(u2mfn_fd, U2MFN_GET_LAST_MFN, 0); + if (*mfn == -1) + return -1; + + return 0; +} + + + +char *u2mfn_alloc_kpage() +{ + char *ret; + if (get_fd()) + return MAP_FAILED; + ret = + mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, u2mfn_fd, 0); + return ret; +} diff --git a/u2mfn/u2mfnlib.h b/u2mfn/u2mfnlib.h new file mode 100644 index 00000000..e64431af --- /dev/null +++ b/u2mfn/u2mfnlib.h @@ -0,0 +1,24 @@ +/* + * 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. + * + */ + +int u2mfn_get_mfn_for_page(long va, int *mfn) ; +int u2mfn_get_last_mfn(int *mfn) ; +char *u2mfn_alloc_kpage(void) ; diff --git a/vchan/Makefile b/vchan/Makefile new file mode 100644 index 00000000..ca7cc724 --- /dev/null +++ b/vchan/Makefile @@ -0,0 +1,39 @@ +# +# 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. +# +# + +CC=gcc +CFLAGS=-g -Wall -I../u2mfn +all: libvchan.so + +libvchan.so : init.o io.o + gcc -shared -o libvchan.so init.o io.o -L ../u2mfn -lu2mfn +init.o: init.c + gcc -fPIC -Wall -g -c init.c +io.o: io.c + gcc -fPIC -Wall -g -c io.c +node: node.o libvchan.so + gcc -g -o node node.o -L. -lvchan -lxenctrl -lxenstore +node-select: node-select.o libvchan.so + gcc -g -o node-select node-select.o -L. -lvchan -lxenctrl -lxenstore +clean: + rm -f *.o *so *~ client server node node-select + + diff --git a/vchan/init.c b/vchan/init.c new file mode 100644 index 00000000..4a3da4f2 --- /dev/null +++ b/vchan/init.c @@ -0,0 +1,224 @@ +/* + * 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 "libvchan.h" +#include "../u2mfn/u2mfnlib.h" + +static int ring_init(struct libvchan *ctrl) +{ + int u2mfn = open("/proc/u2mfn", O_RDONLY); + int mfn; + struct vchan_interface *ring; + ring = (struct vchan_interface *) u2mfn_alloc_kpage (); + + if (ring == MAP_FAILED) + return -1; + + ctrl->ring = ring; + if (u2mfn_get_last_mfn (&mfn) < 0) + return -1; + + ctrl->ring_ref = mfn; + close(u2mfn); + ring->cons_in = ring->prod_in = ring->cons_out = ring->prod_out = + 0; + ring->server_closed = ring->client_closed = 0; + ring->debug = 0xaabbccdd; + 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 devno) +{ + int ret = -1; + struct xs_handle *xs; + char buf[64]; + char ref[16]; + int evfd; + evtchn_port_or_error_t port; + xs = xs_domain_open(); + if (!xs) { + return ret; + } + evfd = xc_evtchn_open(); + if (evfd < 0) + goto fail; + ctrl->evfd = evfd; + // the following hardcoded 0 is the peer domain id + port = xc_evtchn_bind_unbound_port(evfd, 0); + if (port < 0) + goto fail2; + ctrl->evport = port; + snprintf(ref, sizeof ref, "%d", ctrl->ring_ref); + snprintf(buf, sizeof buf, "device/vchan/%d/ring-ref", devno); + if (!xs_write(xs, 0, buf, ref, strlen(ref))) + goto fail2; + snprintf(ref, sizeof ref, "%d", ctrl->evport); + snprintf(buf, sizeof buf, "device/vchan/%d/event-channel", devno); + if (!xs_write(xs, 0, buf, ref, strlen(ref))) + goto fail2; + // wait for the peer to arrive + if (xc_evtchn_pending(evfd) == -1) + goto fail2; + xc_evtchn_unmask(ctrl->evfd, ctrl->evport); + snprintf(buf, sizeof buf, "device/vchan/%d", devno); + xs_rm(xs, 0, buf); + + ret = 0; + fail2: + if (ret) + close(evfd); + fail: + xs_daemon_close(xs); + return ret; +} + +#define dir_select(dir1, dir2) \ + ctrl->wr_cons = &ctrl->ring->cons_##dir1; \ + ctrl->wr_prod = &ctrl->ring->prod_##dir1; \ + ctrl->rd_cons = &ctrl->ring->cons_##dir2; \ + ctrl->rd_prod = &ctrl->ring->prod_##dir2; \ + ctrl->wr_ring = ctrl->ring->buf_##dir1; \ + ctrl->rd_ring = ctrl->ring->buf_##dir2; \ + ctrl->wr_ring_size = sizeof(ctrl->ring->buf_##dir1); \ + ctrl->rd_ring_size = sizeof(ctrl->ring->buf_##dir2) + +/** + 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 devno) +{ + struct libvchan *ctrl = + (struct libvchan *) malloc(sizeof(struct libvchan)); + if (!ctrl) + return 0; + if (ring_init(ctrl)) + return 0;; + if (server_interface_init(ctrl, devno)) + return 0; +/* + We want the same code for read/write functions, regardless whether + we are client, or server. Thus, we do not access buf_in nor buf_out + buffers directly. Instead, in *_init functions, the dir_select + macro assigns proper values to wr* and rd* pointers, so that they + point to correct one out of buf_in or buf_out related fields. +*/ + dir_select(in, out); + ctrl->is_server = 1; + 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 domain, int devno) +{ + int ret = -1; + unsigned int len; + struct xs_handle *xs; + int xcfd; + char buf[64]; + char *ref; + int evfd; + int remote_port; + xs = xs_daemon_open(); + if (!xs) { + return ret; + } + snprintf(buf, sizeof buf, + "/local/domain/%d/device/vchan/%d/ring-ref", domain, + devno); + ref = xs_read(xs, 0, buf, &len); + if (!ref) + goto fail; + ctrl->ring_ref = atoi(ref); + if (!ctrl->ring_ref) + goto fail; + free(ref); + snprintf(buf, sizeof buf, + "/local/domain/%d/device/vchan/%d/event-channel", domain, + devno); + ref = xs_read(xs, 0, buf, &len); + if (!ref) + goto fail; + remote_port = atoi(ref); + if (!remote_port) + goto fail; + free(ref); + xcfd = xc_interface_open(); + if (xcfd < 0) + goto fail; + ctrl->ring = (struct vchan_interface *) + xc_map_foreign_range(xcfd, domain, 4096, + PROT_READ | PROT_WRITE, ctrl->ring_ref); + close(xcfd); + if (ctrl->ring == 0 || ctrl->ring == MAP_FAILED) + goto fail; + evfd = xc_evtchn_open(); + if (evfd < 0) + goto fail; + ctrl->evfd = evfd; + ctrl->evport = + xc_evtchn_bind_interdomain(evfd, domain, remote_port); + if (ctrl->evport < 0 || xc_evtchn_notify(evfd, ctrl->evport)) + close(evfd); + else + ret = 0; + fail: + xs_daemon_close(xs); + 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 = + (struct libvchan *) malloc(sizeof(struct libvchan)); + if (!ctrl) + return 0; + if (client_interface_init(ctrl, domain, devno)) + return 0; +// See comment in libvchan_server_init + dir_select(out, in); + ctrl->is_server = 0; + return ctrl; +} diff --git a/vchan/io.c b/vchan/io.c new file mode 100644 index 00000000..7b524279 --- /dev/null +++ b/vchan/io.c @@ -0,0 +1,159 @@ +/* + * 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 "libvchan.h" +#include +#include +/** + \return How much data is immediately available for reading +*/ +int libvchan_data_ready(struct libvchan *ctrl) +{ + return *ctrl->rd_prod - *ctrl->rd_cons; +} + +/** + \return How much space is available for writing, without blocking +*/ +int libvchan_buffer_space(struct libvchan *ctrl) +{ + return ctrl->wr_ring_size - (*ctrl->wr_prod - *ctrl->wr_cons); +} + +static int do_notify(struct libvchan *ctrl) +{ + return xc_evtchn_notify(ctrl->evfd, ctrl->evport); +} + +/// returns nonzero if the peer has closed connection +int libvchan_is_eof(struct libvchan *ctrl) +{ + if (ctrl->is_server) { + if (ctrl->ring->client_closed) + return -1; + } else { + if (ctrl->ring->server_closed) { + ctrl->ring->client_closed = 1; + do_notify(ctrl); + return -1; + } + + } + return 0; +} + +/// waits for the peer to do any action +/** + \return -1 return value means peer has closed +*/ +int libvchan_wait(struct libvchan *ctrl) +{ + int ret; + ret = xc_evtchn_pending(ctrl->evfd); + if (ret!=-1 && xc_evtchn_unmask(ctrl->evfd, ctrl->evport)) + return -1; + if (ret!=-1 && libvchan_is_eof(ctrl)) + return -1; + return ret; +} + +/** + may sleep (only if no buffer space available); + may write less data than requested; + returns the amount of data processed, -1 on error or peer close +*/ +int libvchan_write(struct libvchan *ctrl, char *data, int size) +{ + int avail, avail_contig; + int real_idx; + while ((avail = libvchan_buffer_space(ctrl)) == 0) + if (libvchan_wait(ctrl) < 0) + return -1; + if (avail > size) + avail = size; + real_idx = (*ctrl->wr_prod) & (ctrl->wr_ring_size - 1); + avail_contig = ctrl->wr_ring_size - real_idx; + if (avail_contig < avail) + avail = avail_contig; + memcpy(ctrl->wr_ring + real_idx, data, avail); + *ctrl->wr_prod += avail; + if (do_notify(ctrl) < 0) + return -1; + return avail; +} + +/** + may sleep (only if no data is available for reading); + may return less data than requested; + returns the amount of data processed, -1 on error or peer close +*/ +int libvchan_read(struct libvchan *ctrl, char *data, int size) +{ + int avail, avail_contig; + int real_idx; + while ((avail = libvchan_data_ready(ctrl)) == 0) + if (libvchan_wait(ctrl) < 0) + return -1; + if (avail > size) + avail = size; + real_idx = (*ctrl->rd_cons) & (ctrl->rd_ring_size - 1); + avail_contig = ctrl->rd_ring_size - real_idx; + if (avail_contig < avail) + avail = avail_contig; + memcpy(data, ctrl->rd_ring + real_idx, avail); + *ctrl->rd_cons += avail; + if (do_notify(ctrl) < 0) + return -1; + return avail; +} + +/** + Wait fot the writes to finish, then notify the peer of closing + On server side, it waits for the peer to acknowledge +*/ +int libvchan_close(struct libvchan *ctrl) +{ + while (*ctrl->wr_prod != *ctrl->wr_cons) + if (libvchan_wait(ctrl) < 0) + return -1; + if (ctrl->is_server) { + ctrl->ring->server_closed = 1; + do_notify(ctrl); + while (!ctrl->ring->client_closed + && libvchan_wait(ctrl) == 0); + } else { + ctrl->ring->client_closed = 1; + do_notify(ctrl); + } + return 0; +} + +/// The fd to use for select() set +int libvchan_fd_for_select(struct libvchan *ctrl) +{ + return ctrl->evfd; +} + +/// Unmasks event channel; must be called before calling select(), and only then +void libvchan_prepare_to_select(struct libvchan *ctrl) +{ + xc_evtchn_unmask(ctrl->evfd, ctrl->evport); +} diff --git a/vchan/libvchan.h b/vchan/libvchan.h new file mode 100644 index 00000000..652284ba --- /dev/null +++ b/vchan/libvchan.h @@ -0,0 +1,60 @@ +/* + * 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 +typedef uint32_t VCHAN_RING_IDX; + +/// struct vchan_interface is placed in memory shared between domains +struct vchan_interface { + // One buffer for each data direction + char buf_in[1024]; + char buf_out[2048]; + // standard consumer/producer interface, one pair per buffer + VCHAN_RING_IDX cons_in, prod_in, cons_out, prod_out; + uint32_t debug; + int client_closed, server_closed; +}; +/// struct libvchan is a control structure, passed to all library calls +struct libvchan { + struct vchan_interface *ring; + uint32_t ring_ref; + /// descriptor to event channel interface + int evfd; + int evport; + VCHAN_RING_IDX *wr_cons, *wr_prod, *rd_cons, *rd_prod; + char *rd_ring, *wr_ring; + int rd_ring_size, wr_ring_size; + int is_server; +}; + +struct libvchan *libvchan_server_init(int devno); + +struct libvchan *libvchan_client_init(int domain, int devno); + +int libvchan_write(struct libvchan *ctrl, char *data, int size); +int libvchan_read(struct libvchan *ctrl, char *data, int size); +int libvchan_wait(struct libvchan *ctrl); +int libvchan_close(struct libvchan *ctrl); +void libvchan_prepare_to_select(struct libvchan *ctrl); +int libvchan_fd_for_select(struct libvchan *ctrl); +int libvchan_is_eof(struct libvchan *ctrl); +int libvchan_data_ready(struct libvchan *ctrl); +int libvchan_buffer_space(struct libvchan *ctrl); diff --git a/vchan/node-select.c b/vchan/node-select.c new file mode 100644 index 00000000..a4f1cdaf --- /dev/null +++ b/vchan/node-select.c @@ -0,0 +1,133 @@ +/* + * 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 "libvchan.h" +#include +#include +#include +#include +int libvchan_write_all(struct libvchan *ctrl, char *buf, int size) +{ + int written = 0; + int ret; + while (written < size) { + ret = libvchan_write(ctrl, buf + written, size - written); + if (ret <= 0) { + perror("write"); + exit(1); + } + written += ret; + } + return size; +} + +int write_all(int fd, char *buf, int size) +{ + int written = 0; + int ret; + while (written < size) { + ret = write(fd, buf + written, size - written); + if (ret <= 0) { + perror("write"); + exit(1); + } + written += ret; + } + return size; +} + + +void usage() +{ + fprintf(stderr, "usage:\n\tnode-select server nodeid\n" + "or\n" "\tnode-select client domainid nodeid\n"); + exit(1); +} + +#define BUFSIZE 5000 +char buf[BUFSIZE]; + +/** + Simple libvchan application, both client and server. + Both sides may write and read, both from the libvchan and from + stdin/stdout (just like netcat). More code is required to avoid + deadlock when both sides write, and noone reads. +*/ + +int main(int argc, char **argv) +{ + int ret; + int libvchan_fd; + struct libvchan *ctrl = 0; + if (argc < 3) + usage(); + if (!strcmp(argv[1], "server")) + ctrl = libvchan_server_init(atoi(argv[2])); + else if (!strcmp(argv[1], "client")) + ctrl = libvchan_client_init(atoi(argv[2]), atoi(argv[3])); + else + usage(); + if (!ctrl) { + perror("libvchan_*_init"); + exit(1); + } + + libvchan_fd = libvchan_fd_for_select(ctrl); + for (;;) { + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(0, &rfds); + FD_SET(libvchan_fd, &rfds); +// libvchan_prepare_to_select(ctrl); + ret = select(libvchan_fd + 1, &rfds, NULL, NULL, NULL); + if (ret < 0) { + perror("select"); + exit(1); + } + if (libvchan_is_eof(ctrl)) + exit(0); + if (FD_ISSET(libvchan_fd, &rfds)) +// we don't care about the result, but we need to do the read to +// clear libvchan_fd pendind state + libvchan_wait(ctrl); + while (libvchan_data_ready(ctrl) > 0) { + ret = libvchan_read(ctrl, buf, BUFSIZE); + if (ret < 0) + exit(0); + write_all(1, buf, ret); + } + if (FD_ISSET(0, &rfds)) { + ret = read(0, buf, BUFSIZE); + if (ret == 0) { + libvchan_close(ctrl); + exit(0); + } + if (ret < 0) { + perror("read 0"); + exit(1); + } +// libvchan_write_all can block; so if both sides write a lot, +// we can deadlock. Need higher level solution; would libvchan_write be ok ? + libvchan_write_all(ctrl, buf, ret); + } + + } +} diff --git a/vchan/node.c b/vchan/node.c new file mode 100644 index 00000000..d739fe7f --- /dev/null +++ b/vchan/node.c @@ -0,0 +1,157 @@ +/* + * 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 "libvchan.h" +#include +#include +#include +#include +#include +int libvchan_write_all(struct libvchan *ctrl, char *buf, int size) +{ + int written = 0; + int ret; + while (written < size) { + ret = libvchan_write(ctrl, buf + written, size - written); + if (ret <= 0) { + perror("write"); + exit(1); + } + written += ret; + } + return size; +} + +int write_all(int fd, char *buf, int size) +{ + int written = 0; + int ret; + while (written < size) { + ret = write(fd, buf + written, size - written); + if (ret <= 0) { + perror("write"); + exit(1); + } + written += ret; + } + return size; +} + +void usage() +{ + fprintf(stderr, "usage:\n\tnode server [read|write] nodeid\n" + "or\n" "\tnode client [read|write] domainid nodeid\n"); + exit(1); +} + +#define BUFSIZE 5000 +char buf[BUFSIZE]; +void reader(struct libvchan *ctrl) +{ + int size; + for (;;) { + size = rand() % (BUFSIZE - 1) + 1; + size = libvchan_read(ctrl, buf, size); + fprintf(stderr, "#"); + if (size < 0) { + perror("read vchan"); + libvchan_close(ctrl); + exit(1); + } + if (size == 0) + break; + size = write_all(1, buf, size); + if (size < 0) { + perror("stdout write"); + exit(1); + } + if (size == 0) { + perror("write size=0?\n"); + exit(1); + } + } +} + +void writer(struct libvchan *ctrl) +{ + int size; + for (;;) { + size = rand() % (BUFSIZE - 1) + 1; + size = read(0, buf, size); + if (size < 0) { + perror("read stdin"); + libvchan_close(ctrl); + exit(1); + } + if (size == 0) + break; + size = libvchan_write_all(ctrl, buf, size); + fprintf(stderr, "#"); + if (size < 0) { + perror("vchan write"); + exit(1); + } + if (size == 0) { + perror("write size=0?\n"); + exit(1); + } + } +} + + +/** + Simple libvchan application, both client and server. + One side does writing, the other side does reading; both from + standard input/output fds. +*/ +int main(int argc, char **argv) +{ + int seed = time(0); + struct libvchan *ctrl = 0; + int wr; + if (argc < 4) + usage(); + if (!strcmp(argv[2], "read")) + wr = 0; + else if (!strcmp(argv[2], "write")) + wr = 1; + else + usage(); + if (!strcmp(argv[1], "server")) + ctrl = libvchan_server_init(atoi(argv[3])); + else if (!strcmp(argv[1], "client")) + ctrl = libvchan_client_init(atoi(argv[3]), atoi(argv[4])); + else + usage(); + if (!ctrl) { + perror("libvchan_*_init"); + exit(1); + } + + srand(seed); + fprintf(stderr, "seed=%d\n", seed); + if (wr) + writer(ctrl); + else + reader(ctrl); + libvchan_close(ctrl); + return 0; +}