Mouse and Keyboard passthrough to Windows HVM

I have found a solution that works but definitely needs to be cleaned up. The performance is great though. For example my use of mkfifo and cat feels very hacky and I imagine there is a better way using file descriptors (like in the init file). Anyways, here is the full setup:

0. Proceed with caution!

Make sure to make a backup copy of qemu-stubdom-linux-full-kernel and qemu-stubdom-linux-full-rootfs in /usr/libexec/xen/boot/ so you can always go back to a working system.

I do not know about the security concerns of this setup.


1. Modifying the stub domain kernel

Using Qubes builder I remade qemu-stubdom-linux-full-kernel by adding a file I called 60-input:

CONFIG_INPUT=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_EVDEV=y

Then copy the kernel to /usr/libexec/xen/boot/


2. Modifying the stub domain rootfs

As stated above follow Neowutran’s gpu guide. For this you don’t need to do the “gpu_” modification but its the same steps for editing the init file. Be sure to edit the full version of stubdomain-linux-full for the kernel and rootfs

mkdir stubroot
cp /usr/libexec/xen/boot/qemu-stubdom-linux-full-rootfs stubroot/qemu-stubdom-linux-full-rootfs.gz
cd stubroot
gunzip qemu-stubdom-linux-full-rootfs.gz
cpio -i -d -H newc --no-absolute-filenames < qemu-stubdom-linux-full-rootfs
rm qemu-stubdom-linux-full-rootfs

edit init just after the usb_args= section to have:

# Setup input proxy devices
mkfifo /tmp/proxykeyboard
mkfifo /tmp/proxymouse

{ cat <>/tmp/proxykeyboard & } | USER=root input-proxy-receiver --keyboard &

USER=root cat /bin/kbhello.txt > /tmp/proxykeyboard &

{ cat <>/tmp/proxymouse & } | USER=root input-proxy-receiver --mouse &

USER=root cat /bin/mhello.txt > /tmp/proxymouse &

evdev_args=$'-object\ninput-linux,id=kbd1,evdev=/dev/input/event1,grab_all=on,repeat=on,\n-object\ninput-linux,id=mou1,evdev=/dev/input/event0,'

Note: You may have to swap event0 and event1 depending on how creates the devices. One could probably use the --name feature for input-proxy-receiver to find the correct event id.

Add $evdev_args to the qemu command at the end of init so it looks like:

qemu -sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
-chardev pipe,path=/tmp/qmp/qemu,id=m -mon chardev=m,mode=control \
-chardev socket,server,nowait,path=/tmp/qemu.qmp,id=m2 -mon chardev=m2,mode=control \
    $dm_args $evdev_args $pa_args $usb_args $kernel &

Input-proxy-reciever needs some info before it creates the uinput device. We need to pass it to it via kbhello.txt and mhello.txt. You can name this whatever you want. To create it run this in dom0 or wherever your keyboard/mouse is located. Immediately stop the program running before moving your mouse or pressing any other keys via CTRL+C:

input-proxy-sender /dev/input/by-id/mykeyboard > kbhello.txt

Repeat for a mouse. Place these files in rootfs/bin or somewhere else.

Copy input-proxy-reciever to the bin folder in stubroot (I just copied this from dom0’s bin. This should probably be compiled in step 1 though).

Then finally put it back together and replace the original rootfs:

find . -print0 | cpio --null -ov \
--format=newc | gzip -9 > ../qemu-stubdom-linux-full-rootfs
sudo mv ../qemu-stubdom-linux-full-rootfs /usr/libexec/xen/boot/

3. Pass a mouse and keyboard to the stub domain
A mouse and keyboard can be passed to the stub domain of the HVM you want via this script. My hvm is called gpu_win10 and it’s stub domain is then gpu_win10-dm.

#!/bin/bash
qrexec-client -d gpu_win10-dm -l 'input-proxy-sender /dev/input/by-id/guest-mouse' root:'cat | cat > /tmp/proxymouse' &
qrexec-client -d gpu_win10-dm -l 'input-proxy-sender /dev/input/by-id/guest-keyboard' root:'cat | cat > /tmp/proxykeyboard' &

My mouse and keyboard are called guest-mouse and guest-keyboard because I am using GitHub - mciupak/libvirt-evdev: Libvirt evdev input service to split my keyboard into two devices: a host and guest device. By passing just the guest to gpu_win10-dm I can now switch back and forth between dom0 and gpu_win10 when I press my scroll lock key.


4. Final Steps and Notes

By default the qemu-stubdom-linux-full-* versions will not be used. In my setup I have my windows hvm qvm-features set to:

audio-model ich6 (this definitely forces use of full)
stubdom-qrexec 1 (this should force usage of full)
gui-emulated 0
gui 0
video-model none

By setting this I also disable input from qubes_drv which does not place nicely with this setup. (see comments below)

I also experience an issue where if too many keys are pressed while in guest mode, the extra keys are sent to the host keyboard which then dom0 reads.

Thank you for the help @h5409j. I will try to reply faster to any issues anyone has or errors I have made.

4 Likes