Hotplugger: Real USB Port Passthrough for VFIO/QEMU!

Related tags

Hardwarehotplugger
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.
Owner
DARKGuy (Alemar)
DARKGuy (Alemar)
Lego Mindstorms EV3 and Lego Spike Prime

Lego Mindstorms EV3 and Lego Spike Prime What is FLL? The FIRST LEGO League Challenge Robotics Tournament challenges students from 9 to 16 years old t

Danimar Campos da Costa 1 Nov 14, 2021
Cascade Drone Swarm Physical Demonstration Project

Cascade Drone Swarm Physical Demonstration Project Table of Contents About The Project Built With Getting Started Prerequisites Installation About The

3 Aug 24, 2022
a weather application for the raspberry pi and the Pimorioni Inky pHAT.

raspi-weather a weather application for the raspberry pi and the Inky pHAT

Derek Caelin 59 Oct 24, 2022
Example Python code for building RPi-controlled robotic systems

RPi Example Code Example Python code for building RPi-controlled robotic systems These python files have been compiled / developed by the Neurobionics

Elliott Rouse 2 Feb 04, 2022
Lenovo Legion 5 Pro 2021 Linux RGB Keyboard Light Controller

Lenovo Legion 5 Pro 2021 Linux RGB Keyboard Light Controller This util allows to drive RGB keyboard light on Lenovo Legion 5 Pro 2021 Laptop Requireme

36 Dec 16, 2022
2D waypoints will be predefined in ROS based robots to navigate to the destination avoiding obstacles.

A number of 2D waypoints will be predefined in ROS based robots to navigate to the destination avoiding obstacles.

Arghya Chatterjee 5 Nov 05, 2022
This Home Assistant custom component adds support for controlling Midea dehumidiferes on local network.

This is a custom component for Home assistant that adds support for Midea dehumidifier appliances via the local area network. midea-dehumidifier-lan H

Nenad Bogojevic 97 Jan 08, 2023
Simples Keylogger para Windows com um autoboot implementado no sistema

MKW Keylogger Keylogger simples para Windos com um autoboot implementado no sistema, o malware irá capturar pressionamentos de tecla e armazená-lo em

3 Jul 03, 2021
Beam designs for infinite Z 3D printers

A 3D printed beam that is as stiff as steel A while ago Naomi Wu 机械妖姬 very kindly sent us one of Creality's infinite-Z belt printers. Lots of people h

RepRap Ltd 105 Oct 22, 2022
Raspberry Pi Pico Escape Room game.

Pico Escape Room Raspberry Pi Pico Escape Room game. Parts Raspberry Pi Pico Set of 2 x 20-pin Headers for Raspberry Pi Pico 4PCS Breadboards Kit Incl

Kevin Thomas 5 Feb 02, 2022
Custom component for MPC-HC for home-assistant

mpc_hc The current mpchc integration in homeassistant violates ADR0004, so it will be deleted from core. This is just the existing integration copied

3 Dec 15, 2022
Scripts for measuring and displaying thermal behavior on Voron 3D printers

Thermal Profiling Measuring gantry deflection and frame expansion This script runs a series of defined homing and probing routines designed to charact

Jon Sanders 30 Nov 27, 2022
A ESP32 project template with a web interface built in React

ESP AP Webserver demo.mp4 This is my experiment with "mobile app development" for the ESP32. The project consists of two parts, the ESP32 code and the

8 Dec 15, 2022
Doughskript interpreter for converting simple command sequences into executable Arduino C++ code.

Doughskript interpreter for converting simple command sequences into executable Arduino C++ code.

Svjatoslav 2 Jan 11, 2022
Python Wrapper for Homeassistant's REST API

HomeassistantAPI Python Wrapper for Homeassistant's REST API Please ⭐️ the repo if you find this project useful or cool! Here is a quick example. from

Nate 29 Dec 31, 2022
Electrolux Pure i9 robot vacuum integration for Home Assistant.

Home Assistant Pure i9 This repository integrates your Electrolux Pure i9 robot vacuum with the smart home platform Home Assistant. The integration co

Niklas Ekman 15 Dec 22, 2022
A raspberrypi tools for python

raspberrypi-tools how to install: first clone this project: git clone https://github.com/Ardumine/rpi-tools.git then go to the folder cd rpi-tools and

1 Jan 04, 2022
A small Python app to converse between MQTT messages and 433MHz RF signals.

mqtt-rf-bridge A small Python app to converse between MQTT messages and 433MHz RF signals. This acts as a bridge between Paho MQTT and rpi-rf. Require

David Swarbrick 3 Jan 27, 2022
Volkswagen ID component for Home Assistant

Volkswagen ID component for Home Assistant This folder contains both a generic Python 3 library for the Volkswagen ID API and a component for Home Ass

55 Jan 07, 2023
Raspberry Pi Pico support for VS Code

Pico-Go VS Code Extension Pico-Go provides code auto-completion and allows you to communicate with your Raspberry Pi Pico board using the built-in REP

Chris Wood 114 Dec 28, 2022