From 4f7fe1676dfd76e20927d280868e8c4dd298f47f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Wed, 29 May 2024 03:14:09 +0200 Subject: [PATCH 1/3] Do not needlessly reattach device to usbip-host If device is already attached to usbip-host, leave it there. This avoids any other driver interacting with the device in the short time (if some module gets loaded at this time for example). QubesOS/qubes-issues# --- src/usb-export | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/usb-export b/src/usb-export index 061947a..5b4d20f 100755 --- a/src/usb-export +++ b/src/usb-export @@ -93,14 +93,22 @@ modprobe usbip-host # Request that both IN and OUT be handled on a single (stdin) socket kill -USR1 "$QREXEC_AGENT_PID" || exit 1 +attach_to_usbip=true # Unbind the device from the driver if [ -d "$devpath/driver" ]; then - printf %s "$busid" > "$devpath/driver/unbind" || exit 1 + old_driver=$(readlink -f "$devpath/driver") + if [ "$old_driver" != "$SYS_USBIP_HOST" ]; then + printf %s "$busid" > "$devpath/driver/unbind" || exit 1 + else + attach_to_usbip= + fi fi # Bind to the usbip-host driver printf 'add %s' "$busid" > "$SYS_USBIP_HOST/match_busid" || exit 1 -echo "$busid" > "$SYS_USBIP_HOST/bind" || exit 1 +if [ -n "$attach_to_usbip" ]; then + echo "$busid" > "$SYS_USBIP_HOST/bind" || exit 1 +fi # One more safety check - make sure the device is available read status < "$devpath/usbip_status" From 9cfe7208c7d7912ca8d7ec5b3895c38f2277e35e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Wed, 29 May 2024 03:23:36 +0200 Subject: [PATCH 2/3] Add mechanism to reset device just before attaching Some device require reset after interacting with previous driver. When device was used with usbip-host before, it took care of that when detaching from previous target (VM). But if it was attached to some other driver in sys-usb before, this needs to be done manually. To limit possible impact, make this opt-in. There are two methods to opt into this feature: 1. Enable it for all devices using (adjust sys-usb name if applicable): qvm-service --enable sys-usb usb-reset-on-attach 2. Enable for specific device by setting QUBES_USB_RESET udev property. QubesOS/qubes-issues#8953 --- rpm_spec/qubes-usb-proxy.spec.in | 1 + src/usb-export | 10 ++++++++++ src/usb-reset | 25 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100755 src/usb-reset diff --git a/rpm_spec/qubes-usb-proxy.spec.in b/rpm_spec/qubes-usb-proxy.spec.in index e3b3939..2dddaf1 100644 --- a/rpm_spec/qubes-usb-proxy.spec.in +++ b/rpm_spec/qubes-usb-proxy.spec.in @@ -31,6 +31,7 @@ make install-vm DESTDIR=${RPM_BUILD_ROOT} /usr/lib/qubes/usb-import /usr/lib/qubes/usb-export /usr/lib/qubes/usb-detach-all +/usr/lib/qubes/usb-reset %changelog @CHANGELOG@ diff --git a/src/usb-export b/src/usb-export index 5b4d20f..ad2ab2b 100755 --- a/src/usb-export +++ b/src/usb-export @@ -108,6 +108,16 @@ fi printf 'add %s' "$busid" > "$SYS_USBIP_HOST/match_busid" || exit 1 if [ -n "$attach_to_usbip" ]; then echo "$busid" > "$SYS_USBIP_HOST/bind" || exit 1 + + # optionally reset the device to clear any state from previous driver + reset_on_attach=$(udevadm info --query=property \ + --value --property=QUBES_USB_RESET --path="$devpath") + if [ -f /run/qubes-service/usb-reset-on-attach ]; then + reset_on_attach=1 + fi + if [ -n "$reset_on_attach" ]; then + /usr/lib/qubes/usb-reset "$devpath" + fi fi # One more safety check - make sure the device is available diff --git a/src/usb-reset b/src/usb-reset new file mode 100755 index 0000000..1b2e20f --- /dev/null +++ b/src/usb-reset @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import sys +import os +import fcntl +from pathlib import Path + +# from /usr/include/linux/usbdevice_fs.h +# _IO('U', 20) +USBDEVFS_RESET = 0x5514 + +def main(): + if len(sys.argv) != 2: + print("Usage: usb-reset sysfs-devpath", file=sys.stderr()) + exit(2) + devpath = sys.argv[1] + uevent = (Path(devpath) / "uevent").read_text() + devname = [line.partition("=")[2] + for line in uevent.splitlines() + if line.startswith("DEVNAME=")][0] + with (Path("/dev") / devname).open("w") as dev_f: + fcntl.ioctl(dev_f, USBDEVFS_RESET, 0) + +if __name__ == "__main__": + main() From 15fa2052469fc8ec3259b6d382b5e6be2cfc8c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Wed, 29 May 2024 03:31:13 +0200 Subject: [PATCH 3/3] Enable reset on attach for Nitrokey 3 Bootloader Fixes QubesOS/qubes-issues#8953 --- Makefile | 2 ++ rpm_spec/qubes-usb-proxy.spec.in | 1 + src/80-qubes-usb-reset.rules | 3 +++ 3 files changed, 6 insertions(+) create mode 100644 src/80-qubes-usb-reset.rules diff --git a/Makefile b/Makefile index 332750d..9065f75 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,8 @@ install-vm: install qubes-rpc/qubes.USB $(DESTDIR)/etc/qubes-rpc install -d $(DESTDIR)/usr/lib/qubes install src/usb-* $(DESTDIR)/usr/lib/qubes + install -d $(DESTDIR)/usr/lib/udev/rules.d + install src/*.rules $(DESTDIR)/usr/lib/udev/rules.d install -d $(DESTDIR)/etc/qubes/suspend-pre.d ln -s ../../../usr/lib/qubes/usb-detach-all \ $(DESTDIR)/etc/qubes/suspend-pre.d/usb-detach-all.sh diff --git a/rpm_spec/qubes-usb-proxy.spec.in b/rpm_spec/qubes-usb-proxy.spec.in index 2dddaf1..e69600b 100644 --- a/rpm_spec/qubes-usb-proxy.spec.in +++ b/rpm_spec/qubes-usb-proxy.spec.in @@ -32,6 +32,7 @@ make install-vm DESTDIR=${RPM_BUILD_ROOT} /usr/lib/qubes/usb-export /usr/lib/qubes/usb-detach-all /usr/lib/qubes/usb-reset +/usr/lib/udev/rules.d/80-qubes-usb-reset.rules %changelog @CHANGELOG@ diff --git a/src/80-qubes-usb-reset.rules b/src/80-qubes-usb-reset.rules new file mode 100644 index 0000000..697b9b8 --- /dev/null +++ b/src/80-qubes-usb-reset.rules @@ -0,0 +1,3 @@ +# Nitrokey 3 Bootloader, requires reset on attach +# See https://github.com/QubesOS/qubes-issues/issues/8953 +SUBSYSTEM=="usb", ENV{ID_VENDOR_ID}=="20a0", ENV{ID_MODEL_ID}=="42dd", ENV{QUBES_USB_RESET}="1"