diff --git a/Makefile b/Makefile index 4717ce0..0ae46dd 100644 --- a/Makefile +++ b/Makefile @@ -12,15 +12,20 @@ help: @echo "make update-repo-installer -- copy dom0 rpms to installer repo" @echo "make clean -- cleanup" -rpms: +rpms: rpms-vm rpms-dom0 + +rpms-vm: rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/core-vm.spec rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/core-vm-kernel-placeholder.spec - rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/core-dom0.spec rpm --addsign \ - $(RPMS_DIR)/x86_64/qubes-core-dom0-$(VERSION_DOM0)*.rpm \ $(RPMS_DIR)/x86_64/qubes-core-vm-*$(VERSION_VM)*.rpm \ $(RPMS_DIR)/x86_64/qubes-core-vm-kernel-placeholder-*.rpm +rpms-dom0: + rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/core-dom0.spec + rpm --addsign \ + $(RPMS_DIR)/x86_64/qubes-core-dom0-$(VERSION_DOM0)*.rpm + rpms-vaio-fixes: rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/core-dom0-vaio-fixes.spec rpm --addsign $(RPMS_DIR)/x86_64/qubes-core-dom0-vaio-fixes-$(VERSION_VAIO_FIXES)*.rpm diff --git a/README.pvusb b/README.pvusb new file mode 100644 index 0000000..419737a --- /dev/null +++ b/README.pvusb @@ -0,0 +1,69 @@ +Dedicated usbvm (optional) +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In dom0, once: + qvm-create -l red usbvm + + # FIXME: use your own PCI device IDs + qvm-pci -a usbvm 00:1d.0 + qvm-pci -a usbvm 00:1d.1 + qvm-pci -a usbvm 00:1d.2 + qvm-pci -a usbvm 00:1d.7 + +After each dom0 reboot: + qvm-start usbvm + +List +~~~~ + +In dom0: + qvm-usb -l + +Example output: + dom0:7-4 0718:061a TDKMedia_Trans-It_Drive_070326AE8AF92D95 (attached to qdvp:0-1) + dom0:7-5 0b05:1706 ASUS_802.11g_WLAN_Drive (attached to netvm:0-1) + dom0:1-1 045e:0084 Microsoft_Basic_Optical_Mouse + usbvm:4-6 05e3:0723 Generic_USB_Storage (attached to qdvp:1-1) + +Attach +~~~~~~ + +In dom0: + qvm-usb -a [--no-auto-detach] :- + +Example: + qvm-usb -a netvm usbvm:4-1 + +Detach +~~~~~~ + +In dom0: + qvm-usb -d :- + +Example: + qvm-usb -d netvm:0-1 + +Known issues +~~~~~~~~~~~~ + +List/attach/detach operations seem to work ok, devices are recognized by the target VM etc. +But actual usage of the attached devices is unstable at best. In fact the only working device +I saw was one USB stick (and this only after it took a minute to time out and reset the bus +couple times). Kernel crashes are normal as well. I have not investigated these issues yet, +I had similar experience with Marek's scripts. + +* System keyboard / mouse are listed and can be detached away +* Virtual USB devices (ones created by PVUSB frontend) may be listed +* The installation/configuration is not persistent, not retained between reboots +* No debugging / logging / audit trail +* When an attached device is physically unplugged, USB port remains mapped but not displayed +in the list. If device is plugged back it continues to work. Unlisted device cannot be detached. +* We are not attaching actual devices, but USB ports (different behavior from VMWare, might be confusing) +* After device is detached from the frontend and returned back to the backend it is not alwayws usable there +* Code changing configuration of pvusb fe/be and vusb bind/unbind helper are located +misc/xl-qvm-usb-attach.py misc/xl-qvm-usb-detach.py misc/vusb-ctl.py. These helpers are +deployed into the backend domain. The initialization code is qubesutils.py in usb_setup(), +should probably also be moved into an external helper. Perhaps the functionality of these +external helpers should be merged into libxl? The is one catch is invokation of vusb helper +in the backend domain -- now it relies on qubes-specific API. + diff --git a/build-deps.list b/build-deps.list new file mode 100644 index 0000000..55e7836 --- /dev/null +++ b/build-deps.list @@ -0,0 +1 @@ +xen-devel-*DIST* diff --git a/misc/qubes_core.modules b/misc/qubes_core.modules index 9f39e79..42ce0fb 100755 --- a/misc/qubes_core.modules +++ b/misc/qubes_core.modules @@ -1,2 +1,3 @@ modprobe evtchn 2>/dev/null || modprobe xen-evtchn modprobe xen-blkback 2> /dev/null || modprobe blkbk +modprobe xen-usbfront 2> /dev/null diff --git a/misc/qubes_usb.rules b/misc/qubes_usb.rules new file mode 100644 index 0000000..5b3c875 --- /dev/null +++ b/misc/qubes_usb.rules @@ -0,0 +1,10 @@ +# Expose all USB devices (except block) via xenstore + +# Handle only USB devices +SUBSYSTEM!="usb", GOTO="qubes_usb_end" + +ACTION=="add", IMPORT{program}="/usr/lib/qubes/usb_add_change" +ACTION=="change", IMPORT{program}="/usr/lib/qubes/usb_add_change" +ACTION=="remove", RUN+="/usr/lib/qubes/usb_remove" + +LABEL="qubes_usb_end" diff --git a/misc/usb_add_change b/misc/usb_add_change new file mode 100755 index 0000000..20691f0 --- /dev/null +++ b/misc/usb_add_change @@ -0,0 +1,40 @@ +#!/bin/sh + +## +## This script is invoked by udev rules whenever USB device appears or +## changes. This happens in usbvm domain (or dom0 if USB controller +## drivers are in dom0). The script records information about available +## USB devices into XS directory, making it available to qvm-usb tool +## running in dom0. +## + +# FIXME: Ignore USB hubs and other wierd devices (see also in usb_remove). +[ "`echo $TYPE | cut -f1 -d/`" = "9" ] && exit 0 +[ "$DEVTYPE" != "usb_device" ] && exit 0 + +# xenstore doesn't allow dot in key name +XSNAME=`basename ${DEVPATH} | tr . _` + +# FIXME: For some devices (my Cherry keyboard) ID_SERIAL does not +# contain proper human-readable name, should find better method to +# build devide description. +#DESC=`python -c "dev='%d-%d' % (int('${BUSNUM}'.lstrip('0')), (int('${DEVNUM}'.lstrip('0'))-1)); from xen.util import vusb_util; print vusb_util.get_usbdevice_info(dev);"` +DESC="${ID_VENDOR_ID}:${ID_MODEL_ID} ${ID_SERIAL}" + +VERSION=`cat /sys/$DEVPATH/version` +if [ "${VERSION}" = " 1.00" -o "${VERSION}" = " 1.10" ] ; then + VERSION=1 +elif [ "${VERSION}" = " 2.00" ] ; then + VERSION=2 +else + # FIXME: silently ignoring devices with unexpected USB version + exit 0 +fi + +XS_KEY="qubes-usb-devices/$XSNAME" + +xenstore-write "$XS_KEY/desc" "$DESC" +xenstore-write "$XS_KEY/usb-ver" "$VERSION" + +# Make sure PVUSB backend driver is loaded. +/sbin/modprobe xen-usbback 2> /dev/null || /sbin/modprobe usbbk diff --git a/misc/usb_remove b/misc/usb_remove new file mode 100755 index 0000000..aa562fa --- /dev/null +++ b/misc/usb_remove @@ -0,0 +1,9 @@ +#!/bin/sh + +# FIXME: Ignore USB hubs. +[ "`echo $TYPE | cut -f1 -d/`" = "9" ] && exit 0 + +NAME=`basename ${DEVPATH} | tr . _` +XS_KEY="qubes-usb-devices/$NAME" + +xenstore-rm "$XS_KEY" diff --git a/misc/vusb-ctl.py b/misc/vusb-ctl.py new file mode 100755 index 0000000..eae621c --- /dev/null +++ b/misc/vusb-ctl.py @@ -0,0 +1,24 @@ +#!/usr/bin/python + +## +## Python script wrapper around xen.util.vusb_util bind_usb_device() and unbind_usb_device() methods +## Run as root in usbvm +## + +from xen.util import vusb_util +import sys +import os + +if len(sys.argv)!=3: + print 'usage: vusb-ctl device' + sys.exit(1) + +device=sys.argv[2] +if sys.argv[1] == 'bind': + vusb_util.bind_usb_device(device) +elif sys.argv[1] == 'unbind': + vusb_util.unbind_usb_device(device) +else: + print "Invalid command, must be 'bind' or 'unbind'" + sys.exit(1) + diff --git a/misc/xl-qvm-usb-attach.py b/misc/xl-qvm-usb-attach.py new file mode 100755 index 0000000..e854229 --- /dev/null +++ b/misc/xl-qvm-usb-attach.py @@ -0,0 +1,48 @@ +#!/usr/bin/python + +## +## This script is for dom0 +## The syntax is modelled after "xl block-attach" +## + +import sys +import os +import xen.lowlevel.xl + + +# parse command line +if (len(sys.argv)<4) or (len(sys.argv)>5): + print 'usage: xl-qvm-usb-attach.py []' + sys.exit(1) + +frontendvm_xid=sys.argv[1] +backendvm_device=sys.argv[2] + +frontend=sys.argv[3].split('-') +if len(frontend)!=2: + print 'Error: frontendvm-device must be in - format' + sys.exit(1) +(controller, port)=frontend + +if len(sys.argv)>4: + backendvm_xid=int(sys.argv[4]) + backendvm_name=xen.lowlevel.xl.ctx().domid_to_name(backendvm_xid) +else: + backendvm_xid=0 + +# FIXME: command injection +os.system("xenstore-write /local/domain/%s/backend/vusb/%s/%s/port/%s '%s'" + % (backendvm_xid, frontendvm_xid, controller, port, backendvm_device)) + +cmd = "/usr/lib/qubes/vusb-ctl.py bind '%s'" % backendvm_device +if backendvm_xid == 0: + os.system("sudo %s" % cmd) +else: + from qubes.qubes import QubesVmCollection + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_reading() + qvm_collection.load() + qvm_collection.unlock_db() + + # launch + qvm_collection.get_vm_by_name(backendvm_name).run("root: %s" % cmd) diff --git a/misc/xl-qvm-usb-detach.py b/misc/xl-qvm-usb-detach.py new file mode 100755 index 0000000..478d8bb --- /dev/null +++ b/misc/xl-qvm-usb-detach.py @@ -0,0 +1,49 @@ +#!/usr/bin/python + +## +## This script is for dom0 +## The syntax is modelled after "xl block-attach" +## FIXME: should be modelled after block-detach instead +## + +import sys +import os +import xen.lowlevel.xl + +# parse command line +if (len(sys.argv)<4) or (len(sys.argv)>5): + print 'usage: xl-qvm-usb-detach.py []' + sys.exit(1) + +frontendvm_xid=sys.argv[1] +backendvm_device=sys.argv[2] + +frontend=sys.argv[3].split('-') +if len(frontend)!=2: + print 'Error: frontendvm-device must be in - format' + sys.exit(1) +(controller, port)=frontend + +if len(sys.argv)>4: + backendvm_xid=int(sys.argv[4]) + backendvm_name=xen.lowlevel.xl.ctx().domid_to_name(backendvm_xid) +else: + backendvm_xid=0 + +cmd = "/usr/lib/qubes/vusb-ctl.py unbind '%s'" % backendvm_device +if backendvm_xid == 0: + os.system("sudo %s" % cmd) +else: + from qubes.qubes import QubesVmCollection + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_reading() + qvm_collection.load() + qvm_collection.unlock_db() + + # launch + qvm_collection.get_vm_by_name(backendvm_name).run("root: %s" % cmd) + +# FIXME: command injection +os.system("xenstore-write /local/domain/%s/backend/vusb/%s/%s/port/%s ''" + % (backendvm_xid, frontendvm_xid, controller, port)) + diff --git a/rpm_spec/core-vm.spec b/rpm_spec/core-vm.spec index 7b202d3..64884ea 100644 --- a/rpm_spec/core-vm.spec +++ b/rpm_spec/core-vm.spec @@ -113,9 +113,12 @@ install -D misc/xenstore-watch $RPM_BUILD_ROOT/usr/bin/xenstore-watch-qubes install -d $RPM_BUILD_ROOT/etc/udev/rules.d install -m 0644 misc/qubes_memory.rules $RPM_BUILD_ROOT/etc/udev/rules.d/50-qubes_memory.rules install -m 0644 misc/qubes_block.rules $RPM_BUILD_ROOT/etc/udev/rules.d/99-qubes_block.rules +install -m 0644 misc/qubes_usb.rules $RPM_BUILD_ROOT/etc/udev/rules.d/99-qubes_usb.rules install -d $RPM_BUILD_ROOT/usr/lib/qubes/ install misc/qubes_download_dom0_updates.sh $RPM_BUILD_ROOT/usr/lib/qubes/ install misc/{block_add_change,block_remove,block_cleanup} $RPM_BUILD_ROOT/usr/lib/qubes/ +install misc/{usb_add_change,usb_remove} $RPM_BUILD_ROOT/usr/lib/qubes/ +install misc/vusb-ctl.py $RPM_BUILD_ROOT/usr/lib/qubes/ install misc/qubes_trigger_sync_appmenus.sh $RPM_BUILD_ROOT/usr/lib/qubes/ install -D -m 0644 misc/qubes_trigger_sync_appmenus.action $RPM_BUILD_ROOT/etc/yum/post-actions/qubes_trigger_sync_appmenus.action mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes @@ -209,7 +212,7 @@ remove_ShowIn () { for F in abrt-applet deja-dup-monitor imsettings-start krb5-auth-dialog pulseaudio restorecond sealertauto gnome-power-manager gnome-sound-applet gnome-screensaver orca-autostart; do if [ -e /etc/xdg/autostart/$F.desktop ]; then remove_ShowIn $F - echo 'NotShowIn=QUBES' >> /etc/xdg/autostart/$F.desktop + echo 'NotShowIn=QUBES;' >> /etc/xdg/autostart/$F.desktop fi done @@ -217,7 +220,7 @@ done for F in gcm-apply ; do if [ -e /etc/xdg/autostart/$F.desktop ]; then remove_ShowIn $F - echo 'NotShowIn=DisposableVM' >> /etc/xdg/autostart/$F.desktop + echo 'NotShowIn=DisposableVM;' >> /etc/xdg/autostart/$F.desktop fi done @@ -312,6 +315,10 @@ do continue fi + if [ $(basename $f) == "99-qubes_usb.rules" ] ; then + continue + fi + if [ $(basename $f) == "90-hal.rules" ] ; then continue fi @@ -383,6 +390,7 @@ rm -rf $RPM_BUILD_ROOT /etc/udev/rules.d/50-qubes_memory.rules /etc/udev/rules.d/99-qubes_block.rules /etc/udev/rules.d/99-qubes_network.rules +/etc/udev/rules.d/99-qubes_usb.rules /etc/xen/scripts/vif-route-qubes /etc/yum.conf.d/qubes-proxy.conf /etc/yum.repos.d/qubes.repo @@ -399,6 +407,9 @@ rm -rf $RPM_BUILD_ROOT /usr/lib/qubes/block_add_change /usr/lib/qubes/block_cleanup /usr/lib/qubes/block_remove +/usr/lib/qubes/usb_add_change +/usr/lib/qubes/usb_remove +/usr/lib/qubes/vusb-ctl.py* /usr/lib/qubes/dispvm-prerun.sh /usr/lib/qubes/sync-ntp-clock /usr/lib/qubes/prepare-suspend diff --git a/test/block-001-list-attach-use-detach b/test/block-001-list-attach-use-detach new file mode 100755 index 0000000..84be5fb --- /dev/null +++ b/test/block-001-list-attach-use-detach @@ -0,0 +1,50 @@ +#!/bin/sh + +tmpf=`mktemp` + +## === List +echo 'qvm-block -l' +qvm-block -l > $tmpf + +cat <<'END' | diff -u - $tmpf +netvm:sda STORAGE_DEVICE () 0 B +dom0:sdb1 Cruzer () 3 GiB +dom0:sdb Cruzer () 3 GiB +END + +## === Attach +echo 'qvm-block -a' +qvm-block -a work dom0:sdb | diff -u /dev/null - + +## === List again +echo 'qvm-block -l' +qvm-block -l > $tmpf + +cat <<'END' | diff -u - $tmpf +netvm:sda STORAGE_DEVICE () 0 B +dom0:sdb1 Cruzer () 3 GiB +dom0:sdb Cruzer () 3 GiB (attached to 'work' as 'xvdi') +END + +## === Use +echo 'qvm-run work fdisk' +qvm-run -qp work 'su - root -c "fdisk -l /dev/xvdi"' > $tmpf + +cat <<'END' | diff -u - $tmpf + +Disk /dev/xvdi: 4022 MB, 4022337024 bytes +124 heads, 62 sectors/track, 1021 cylinders, total 7856127 sectors +Units = sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes +Disk identifier: 0x3963a77b + + Device Boot Start End Blocks Id System +/dev/xvdi1 * 62 7849447 3924693 c W95 FAT32 (LBA) +END + +## === Detach +echo 'qvm-block -d' +qvm-block -d work dom0:sdb1 | diff -u /dev/null - + +rm $tmpf diff --git a/test/pvusb-001-mouse-kbd-attached b/test/pvusb-001-mouse-kbd-attached new file mode 100755 index 0000000..ec51861 --- /dev/null +++ b/test/pvusb-001-mouse-kbd-attached @@ -0,0 +1,14 @@ +#!/bin/sh + +tmpf=`mktemp` + +sudo xenstore-ls -f qubes-usb-devices | sort > $tmpf + +cat << 'END' | diff -u - $tmpf +qubes-usb-devices/2-1 = "" +qubes-usb-devices/2-1/desc = "046a:0021 046a_0021" +qubes-usb-devices/2-2 = "" +qubes-usb-devices/2-2/desc = "045e:0745 Microsoft_Microsoft\xc2\xae_Nano_Transceiver_v1.0" +END + +rm $tmpf diff --git a/test/pvusb-002-mouse-kbd-usbstick-wlan-attached b/test/pvusb-002-mouse-kbd-usbstick-wlan-attached new file mode 100755 index 0000000..6705329 --- /dev/null +++ b/test/pvusb-002-mouse-kbd-usbstick-wlan-attached @@ -0,0 +1,18 @@ +#!/bin/sh + +tmpf=`mktemp` + +sudo xenstore-ls -f qubes-usb-devices | sort > $tmpf + +cat << 'END' | diff -u - $tmpf +qubes-usb-devices/2-1 = "" +qubes-usb-devices/2-1/desc = "046a:0021 046a_0021" +qubes-usb-devices/2-2 = "" +qubes-usb-devices/2-2/desc = "045e:0745 Microsoft_Microsoft\xc2\xae_Nano_Transceiver_v1.0" +qubes-usb-devices/7-1 = "" +qubes-usb-devices/7-1/desc = "07d1:3c0a Ralink_11n_Adapter_1.0" +qubes-usb-devices/7-5 = "" +qubes-usb-devices/7-5/desc = "0781:5530 SanDisk_Cruzer_1942531DB09038A6" +END + +rm $tmpf diff --git a/test/pvusb-003-list-attach-use-detach b/test/pvusb-003-list-attach-use-detach new file mode 100755 index 0000000..e0036de --- /dev/null +++ b/test/pvusb-003-list-attach-use-detach @@ -0,0 +1,67 @@ +#!/bin/sh + +tmpf=`mktemp` +domu='qdvp' + +## === List +echo 'qvm-usb -l' +qvm-usb -l | sort > $tmpf + +cat <<'END' | diff -bu - $tmpf +dom0:2-1 046a:0021 046a_0021 +dom0:2-2 045e:0745 Microsoft_Microsoft®_Nano_Transceiver_v1.0 +dom0:7-1 07d1:3c0a Ralink_11n_Adapter_1.0 +dom0:7-5 0781:5530 SanDisk_Cruzer_1942531DB09038A6 +END + +## === Attach +echo 'qvm-usb -a' +qvm-usb -a $domu dom0:7-5 | diff -bu /dev/null - +#echo 'pvusb-script/usb-attach.py' +#sudo /home/abb/pvusb-scripts/usb-attach.py 7-5 3 1 + +## === List again +echo 'qvm-usb -l' +qvm-usb -l | sort > $tmpf + +# FIXME +cat <<'END' | diff -bu - $tmpf +dom0:2-1 046a:0021 046a_0021 +dom0:2-2 045e:0745 Microsoft_Microsoft®_Nano_Transceiver_v1.0 +dom0:7-1 07d1:3c0a Ralink_11n_Adapter_1.0 +dom0:7-5 0781:5530 SanDisk_Cruzer_1942531DB09038A6 +END + +read -p 'Press ENTER to continue' + +## === Use: try ls +echo 'qvm-run $domu ls /dev/sda' +qvm-run -p $domu 'ls /dev/sda' > $tmpf +cat <<'END' | diff -bu - $tmpf +/dev/sda +END + +## === Use: try fdisk +echo 'qvm-run $domu fdisk' +qvm-run -p $domu 'su - root -c "fdisk -l /dev/sda"' > $tmpf + +cat <<'END' | diff -bu - $tmpf + +Disk /dev/sda: 4022 MB, 4022337024 bytes +124 heads, 62 sectors/track, 1021 cylinders, total 7856127 sectors +Units = sectors of 1 * 512 = 512 bytes +Sector size (logical/physical): 512 bytes / 512 bytes +I/O size (minimum/optimal): 512 bytes / 512 bytes +Disk identifier: 0x3963a77b + + Device Boot Start End Blocks Id System +/dev/sda1 * 62 7849447 3924693 c W95 FAT32 (LBA) +END + +## === Detach +#echo 'qvm-usb -d' +#qvm-usb -d $domu:7-5 | diff -bu /dev/null - +echo 'pvusb-script/usb-detach.py' +sudo /home/abb/pvusb-scripts/usb-detach.py 7-5 4 1 + +rm $tmpf diff --git a/version_vm b/version_vm index c8a481c..3e3c2f1 100644 --- a/version_vm +++ b/version_vm @@ -1 +1 @@ -1.7.46 +2.1.1 diff --git a/vm-init.d/qubes_core b/vm-init.d/qubes_core index c5fe5d2..4830587 100755 --- a/vm-init.d/qubes_core +++ b/vm-init.d/qubes_core @@ -17,6 +17,8 @@ start() # Set permissions to /proc/xen/xenbus, so normal user can use xenstore-read chmod 666 /proc/xen/xenbus + # Set permissions to files needed to listen at vchan + chmod 666 /proc/u2mfn /dev/xen/evtchn mkdir -p /var/run/xen-hotplug diff --git a/vm-systemd/qubes-sysinit.sh b/vm-systemd/qubes-sysinit.sh index b054407..f0b098a 100755 --- a/vm-systemd/qubes-sysinit.sh +++ b/vm-systemd/qubes-sysinit.sh @@ -25,6 +25,8 @@ mkdir -p /var/run/xen-hotplug # Set permissions to /proc/xen/xenbus, so normal user can use xenstore-read chmod 666 /proc/xen/xenbus +# Set permissions to files needed to listen at vchan +chmod 666 /proc/u2mfn /dev/xen/evtchn # Set default services depending on VM type TYPE=`$XS_READ qubes_vm_type 2> /dev/null`