Hotplugger: Real USB Port Passthrough for VFIO/QEMU!

Related tags

Hardware hotplugger
Overview

Hotplugger: Real USB Port Passthrough for VFIO/QEMU!

Welcome to Hotplugger! This app, as the name might tell you, is a combination of some scripts (python, yaml, udev rules and some QEMU args) to allow you to pass through an actual USB port into a VM. Instead of passing the USB root hub (which could have the side effect of passing all the ports, including the ones you didn't want to) or another PCIe hub or something, you can just pass a specific USB port to a VM and have the others free for anything else. Plus, it saves you from using the vfio-pci driver for the USB root hub, so you can keep using it for evdev or other things on the VM host.

Requirements

  • monitor.py and hotplugger.py require Python 3
  • Only tested with QEMU 5.0.0. Untested with older or newer versions.

Quick start (Ubuntu 20.10)

  1. git clone https://github.com/darkguy2008/hotplugger.git

  2. (Optional) run python3 monitor.py and follow the prompts. Basically once you hit Enter you have to plug and unplug an USB device (a thumbdrive or audio device preferred) into the USB ports that you want to know their DEVPATH route from. This will help you identify them so you can write them into config.yaml in the ports array. This array only accepts DEVPATH routes that UDEV generates.

  3. Edit config.yaml. It must stay in the same folder as monitor.py and hotplugger.py. Look at the current example: It's set for a Windows VM (the name doesn't matter, as long as it's unique within the entries of the same file). Make sure the socket property matches the file path of the QEMU chardev device pointing to an Unix domain socket file and in the ports array put the list of the udev DEVPATH of the USB ports you want to pass through to that VM:

    virtual_machines:
    
      windows:
        socket: /home/dragon/vm/test/qmp-sock
        ports:
          - /devices/pci0000:00/0000:00:14.0/usb3/3-1
          - /devices/pci0000:00/0000:00:14.0/usb3/3-2
          - /devices/pci0000:00/0000:00:14.0/usb4/4-1
          - /devices/pci0000:00/0000:00:14.0/usb4/4-2
    
  4. Create an /etc/udev/rules.d/99-zzz-local.rules file with the following content:

    SUBSYSTEM=="usb", ACTION=="add", RUN+="/bin/bash -c 'python3 /path-to-hotplugger/hotplugger.py >> /tmp/hotplugger.log' 2>&1"
    SUBSYSTEM=="usb", ACTION=="remove", RUN+="/bin/bash -c 'python3 /path-to-hotplugger/hotplugger.py >> /tmp/hotplugger.log' 2>&1"
    

    Make sure to change path-to-hotplugger with the path where you cloned the repo to, or installed the package. It can be simplified, but this one is useful in case you want to debug and see what's going on. Otherwise, proceed with a simpler file:

    SUBSYSTEM=="usb", ACTION=="add", RUN+="/bin/bash -c 'python3 /path-to-hotplugger/hotplugger.py'"
    SUBSYSTEM=="usb", ACTION=="remove", RUN+="/bin/bash -c 'python3 /path-to-hotplugger/hotplugger.py'"
    
  5. Create the QMP monitor Unix domain socket if you haven't already in your QEMU args. I use this:

    -chardev socket,id=mon1,server,nowait,path=./qmp-sock
    -mon chardev=mon1,mode=control,pretty=on
    
  6. Have a coffee!

Libvirt setup

This is a work in progress, but here's some steps to get you started:

  1. Edit your VM's XML config like this:

    1. <domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
        <name>QEMUGuest1name>
        <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809uuid>
        ...
        <qemu:commandline>
          <qemu:arg value='-chardev'/>
          <qemu:arg value='socket,id=mon1,server,nowait,path=/tmp/my-vm-sock'/>
          <qemu:arg value='-mon'/>
          <qemu:arg value='chardev=mon1,mode=control,pretty=on'/>
        qemu:commandline>
      domain>

      Add the xmlns attribute and the QEMU commandline arguments like that. The /tmp/my-vm-sock is the name of an unix domain socket. You can use any, just make sure to also put the same path in the config.yaml file.

  2. If you get a permissions issue, edit /etc/libvirt/qemu.conf and add security_driver = "none"to it to fix apparmor being annoying about it.

How it works

  1. The udev rule launches the script on every USB event. For each USB add/remove action there's around 3 to 5+ events. This allows the app to act at any step in the action lifecycle.
  2. In the first step it gets the kernel environment variables from udev and stores them in a temp file. In those variables, the DEVPATH, the DEVNUM (host address in QEMU, it seems to change and is sequential...) and the BUSNUM (bus address in QEMU) are captured. For the subsequent events, the following steps are run:
    1. It requests QEMU through the Unix socket and the info usbhost QMP command the USB info from the host. This gives it an extra field: The host port where the device is also connected to. Since I got the host and bus addresses in the first event, I can use that to parse through the info usbhost command's output and find the port connected to the device.
    2. If the port is found, using the device_add command, a new usb-host device is added using the USB bus and port we got in the previous step, and assigns it a predictable ID that it can use to unplug the device afterwards. To add this of course, the VM should have a usb-xhci device I think. Not sure if it's required or not, but I prefer to add it as I have USB 3.0 ports and devices.
    3. The temp file is cleared once the device_add command has run successfully.

Steps 2.1, 2.2 and 2.3 are run on every udev event. For instance, for an audio device it gets 3 or 4 events: One for the HID device, and two or so for the audio devices. My audio device (Corsair Void Elite Wireless) has both stereo audio and a communications device (mono audio, for mic) so for a single dongle like that I get those many events. Since these steps are ran on all the events, there's multiple chances to do the hotplug action. When one of them succeeds, the others will silently fail as QEMU will say that the same device ID is being used, so all is good.

Troubleshooting

If for some reason the app doesn't seem to work, try these methods:

  • Remove the /tmp folder where hotplugger.py is located
  • Reboot the computer
  • Reboot udev: sudo udevadm control --reload-rules && sudo udevadm trigger
  • View udev's logfile: sudo service udev restart && sudo udevadm control --log-priority=debug && journalctl -f | grep -i hotplugger
  • If you want to see what will be run when you plug a device, try with this command to simulate an udev event: udevadm test $(udevadm info -a --path=/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0) --action=add replacing --path with the path of the USB port down to the device itself (in this case, I had a device connected to the usb3/3-1 port, identified as 3-1:1.0.

Thank you!

A lot of work and sleepless nights were involved in this procedure, so if this app helps you in any way or another, please consider sending a small donation, it helps a lot in these tough times!

Changelog

(2020-02-05)

  • Initial changelog writing
  • App was refactored a bit with improved python mad skillz. It also seems to be a bit more stable and robust, it doesn't hang much anymore and USB detection seems to work better. This is due to the fact that I added a stupid 1-second delay after all the USB UDEV events have gone through. Since there's no way to know when UDEV has "finished" sending all the events (and there could be a lot more) the commands being sent to QEMU to add the device will have to wait 1 second now. While it's not ideal, it should be enough to avoid a VM hanging up and I can live with that.
You might also like...
Unofficial Playdate reverse-engineering notes/tools - covers file formats, server API and USB commands

Unofficial Playdate reverse-engineering notes/tools - covers file formats, server API and USB commands ⚠️ This documentation is unofficial and is not

Port of Uxn to digital hardware in the Logisim simulator
Port of Uxn to digital hardware in the Logisim simulator

Uxn-Logisim Implements the Uxn instruction set in digital hardware. Very WIP. Contents cpu.circ - The Logisim file microcode.mc - Microcode source fil

LedFx is a network based LED effect controller with support for advanced real-time audio effects
LedFx is a network based LED effect controller with support for advanced real-time audio effects

Welcome to LedFx ✨ -Making music come alive! LedFx website: https://ledfx.app/ What is LedFx? What LedFx offers is the ability to take audio input, an

The ABR Control library is a python package for the control and path planning of robotic arms in real or simulated environments.
The ABR Control library is a python package for the control and path planning of robotic arms in real or simulated environments.

The ABR Control library is a python package for the control and path planning of robotic arms in real or simulated environments. ABR Control provides API's for the Mujoco, CoppeliaSim (formerly known as VREP), and Pygame simulation environments, and arm configuration files for one, two, and three-joint models, as well as the UR5 and Kinova Jaco 2 arms. Users can also easily extend the package to run with custom arm configurations. ABR Control auto-generates efficient C code for generating the control signals, or uses Mujoco's internal functions to carry out the calculations.

Uses the Duke Energy Gateway to import near real time energy usage into Home Assistant
Uses the Duke Energy Gateway to import near real time energy usage into Home Assistant

Duke Energy Gateway This is a custom integration for Home Assistant. It pulls near-real-time energy usage from Duke Energy via the Duke Energy Gateway

It is a program that displays the current temperature of the GPU and CPU in real time and stores the temperature history.
It is a program that displays the current temperature of the GPU and CPU in real time and stores the temperature history.

HWLogger It is a program that displays the current temperature of the GPU and CPU in real time and stores the temperature history. Sample Usage Run HW

2021 Real Robot Challenge Phase2 attemp
2021 Real Robot Challenge Phase2 attemp

Real_Robot_Challenge_Phase2_AE_attemp We(team name:thriftysnipe) are the first place winner of Phase1 in 2021 Real Robot Challenge. Please see this pa

NYCT-GTFS - Real-time NYC subway data parsing for humans

NYCT-GTFS - Real-time NYC subway data parsing for humans This python library provides a human-friendly, native python interface for dealing with the N

Real-time Coastal Monitoring at the University of Hawaii at Manoa

Coastal Monitoring at the University of Manoa Source code for Beaglebone/RPi-based data loggers, shore internet gateways, and web server. Software dev

Comments
  • README: Update QEMU deprecated implicit boolean options

    README: Update QEMU deprecated implicit boolean options

    The implicit boolean options format (e.g. nowait) is deprecated as QEMU 6.0; the explicit format is 5.x-compatible, so it can be used.

    Also updated the compatibility note (v6.0.0 has been tested and confirmed to work).

    opened by 64kramsystem 1
  • Some improvements to the monitor script

    Some improvements to the monitor script

    I made it so that it prints out only the shortest entries. I changed your unique() function to be a little shorter but added another function that removes longer entries. I also removed a bit where you ask to hit ENTER to continue since this step takes a little bit of time without the necessity - you can just unplug the device you have in the port or plug the device into the port, and it will register the port correctly on any of these actions. I also added saving the found ports to a file so that you don't lose them in the process.

    opened by Iceburgino 0
  • Logs seem good but no USB on Debian guest?

    Logs seem good but no USB on Debian guest?

    Versions

    QEMU emulator version 5.0.0 (Debian 1:5.0-5ubuntu6~ppa00) Host: Ubuntu 20.04 Guest: Debian 11

    Description

    USB not visible on guest. I cannot see anything obvious from the logs but because I do not have much virtualisation experience I assume I missed a step in the configuration that is preventing the USB drive from appearing on debian.

    Steps to Reproduce

    I was receiving errors for xhci not being found before I read darkguy2008/hotplugger#3 and inserted the same lines is using before the qemu arguments from the readme:

      <qemu:commandline>
      ...
        <qemu:arg value="-device"/>
        <qemu:arg value="nec-usb-xhci,id=xhci0"/>
        <qemu:arg value="-device"/>
        <qemu:arg value="nec-usb-xhci,id=xhci1"/>
      </qemu:commandline>
    </domain>
    

    The readme does mention the XML is a work in progress but can you please confirm whether it is correct to add these lines and if anything else is required for a basic setup using virt-manager? Initially this resulted in the same device not found error I can see from the log VladislavF uploaded but some combination of purging everything related to qemu-kvm, libvirt, virtsh, virt-manager, and reinstalling from the default repo, backports (as qemu is v4.x on 20.04), and a third party repo lead me to many new errors but after many iterations now with none that are obvious to me and in fact from the logs to me at least it looks good. Unfortunately the issue remains, no USB visible on Debian although it seems like I am close!

    I cannot say exactly what stopped me receiving that error but I can outline the general steps to setup after the latest purge so that hopefully you can easily spot what I am missing and it may also help someone else.

    • Add ppa:jacob/virtualisation for qemu v5 without the headache of compiling dependencies.

    • sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virt-manager

    • If encountering the 'a storage path must be specified' bug workaround this by moving the installer ISO to the location you want to use for storage, start again selecting it there, and then you should be able to create a volume for storage.

    • Install Debian 11 without changing or adding anything else virt manager.

    My monitor output using a USB 2 and USB 3 keys was:

    DEVPATH=/devices/pci0000:00/0000:00:14.0/usb3/3-2
    DEVPATH=/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.0
    DEVPATH=/devices/pci0000:00/0000:00:14.0/usb4/4-2
    DEVPATH=/devices/pci0000:00/0000:00:14.0/usb4/4-2/4-2:1.0
    
    • config.yaml:
    virtual_machines:
    
      debian:
        socket: /tmp/my-vm-sock
        delay: 1
        hubs:
          - xhci0.0
          - xhci1.0
        ports:
          - /devices/pci0000:00/0000:00:14.0/usb3/3-2
          - /devices/pci0000:00/0000:00:14.0/usb4/4-2
    
    

    Leave the USB key disconnected.

    • 99-zzz-local.rules:
    SUBSYSTEM=="usb", ACTION=="add", RUN+="/bin/bash -c 'python3 /home/anonymousaard/hotplugger/hotplugger.py >> /tmp/hotplugger.log' 2>&1"
    SUBSYSTEM=="usb", ACTION=="remove", RUN+="/bin/bash -c 'python3 /home/anonymousaard/hotplugger/hotplugger.py >> /tmp/hotplugger.log' 2>&1"
    
    • Edit /etc/libvirt/qemu/debian.xml
    export EDITOR=nano
    sudo virsh edit debian
    

    debian.xml.txt

    • Edit qemu.conf as per the readme to workaround the apparmor issue.

    • Complete the troubleshooting steps deleting hotplugger/tmp, rebooting the system, rebooting udev, and viewing the udev's logfile.

    Actual result:

    • Boot Debian and once logged connect the USB key to the port and after observe that the host sees it fine but there is no sign of it on Debian. Do this a second time to show the udev log without error after the reboot.

    hotplugger.log udev.log libvirt_debian.log

    Expected result:

    USB visible on Debian guest.

    Reproduces how often:

    Always.

    Can you please tell me how I can resolve this?

    opened by AnonymousAard 0
  • No Devices Connect

    No Devices Connect

    Hello,

    I am not sure where else to ask, so I'm just opening an issue here.

    Problem: TL;DR upon following all instructions, no devices connect to my Windows 10 VM. I have tried with a USB kvm switch as well as a USB3.0 thumb drive with the same results.

    Detail: I am trying to switch a mouse and keyboard with a USB KVM switch. I realize this may be mildly more complicated than the intended purpose here, but since the switch is basically just a hub, I figured adding all the ports that show up in the monitor script should work. Heck, even just a single device hot-plugging would be good, but I can't get that either.

    It seems to be failing to remove the USB devices, but I imagine that maybe be because they never attach in the first place. Like I mentioned above, result is the same with just a single USB drive.

    Windows sees the xhci controllers added by the command line, but has absolutely no reaction upon connecting or disconnecting USB devices from the ports I've added to the config. The QMP socket file shows up and the scripts don't seem to have any issues with it. I'm just confused since there doesn't seem to be any indication of any issues anywhere except for the disconnect failures. The devices in question always just end up connecting to the host.

    OS: I'm on Ubuntu 20.04 with KVM/QEMU 5.0.

    Steps to reproduce: Simply follow all instructions, and attempt to connect a USB device via hotplug.

    Logs are attached. Thank you for your help! udev.log hotplugger.log win10.xml.txt config.yml.txt

    opened by VladislavF 3
Owner
DARKGuy (Alemar)
DARKGuy (Alemar)
LifeSaver automatically, periodically saves USB flash drive data into the PC

LifeSaver automatically, periodically saves USB flash drive data into the PC. Theoriticaly it will work with any any connected drive ex - Hard Disk ,SSD ... But, can't handle Backing up multipatition drives. I can guess, but cannot be sure of, how it will react to multipartiton system.

siddharth dhaka 4 Sep 26, 2021
LUNA: a USB multitool & nMigen library

LUNA is a full toolkit for working with USB using FPGA technology; and provides hardware, gateware, and software to enable USB applications.

Great Scott Gadgets 750 Dec 28, 2022
Monitor Live USB Plug In & Plug Out Events

I/O - Live USB Monitoring Author: Jonathan Scott @jonathandata1 Date: 3/13/2021 CURRENT VERSION 1.0 This is just a simple bash script that calls a pyt

Jonathan Scott 17 Dec 3, 2022
CircuitPython library for the CH559 USB to Serial chip

CH559 (USB to Serial) CircuitPython Library Why? Because you might want to get keyboard/mouse/gamepad/HID input into your CircuitPython projects witho

Guy Dupont 3 Nov 19, 2022
USB Rubber Ducky with the Rasberry Pi pico microcontroller

pico-ducky Install Install and have your USB Rubber Ducky working in less than 5 minutes. Download CircuitPython for the Raspberry Pi Pico. Plug the d

AnOnYmOus001100 3 Oct 8, 2022
Turn your Raspberry Pi Pico into a USB Rubber Ducky

pico-ducky Turn your Raspberry Pi Pico into a USB Rubber Ducky Install Requirements CircuitPython for the Raspberry Pi Pico adafruit-circuitpython-bun

Konstantinos 5 Nov 8, 2022
Turns a compatible Raspberry Pi device into a smart USB drive for PS4/PS5.

PSBerry A WIP project for Raspberry Pi, which turns a compatible RPI device into a smart USB drive for PS4/PS5. Allows for save management of PS4 game

Filip Tomaszewski 2 Jan 15, 2022
Example code to sending USB Gadget multimedia keys via Python

Send Multimedia USB HID Keys via Python As an USB Gadget in Linux This gives a simple script with zero dependencies that can easily run on any Linux d

DevOps Nirvana 2 Jan 2, 2023
A simple portable USB MIDI controller based on Raspberry-PI Pico and a 16-button keypad, written in Circuit Python

RPI-Pico-16-BTn-MIDI-Controller-using-CircuitPython A simple portable USB MIDI controller based on Raspberry-PI Pico, written in Circuit Python. Link

Rounak Dutta 3 Dec 4, 2022
Sticklog2heatmap - Draw a heatmap of RC sticks from OpenTX logs or USB HID device

sticklog2heatmap Draw a heatmap of RC sticks from OpenTX logs or USB HID device

null 2 Feb 2, 2022