Mouse and Keyboard passthrough to Windows HVM

I am trying to pass a keyboard and mouse to a windows hvm (or any hvm) via libvirt xml config. For reference, I do not want to use a USB kvm or use the USB proxy. I do not want the hvm to have control of the usb device. I want to pass this mouse/keyboard inputs via software only. I cannot seem to find a lot of information about passing mouse/keyboard in Xen or qubes specifically. Could the stubdom-linux may be getting in the way?

I have added a devices block to extend my hvm’s xml libvirt config via: Custom libvirt config — core-admin v4.2.5-0-g5b51295e-dirty documentation. The windows domain will start but will not show any new devices.

When an hvm is being displayed in qubes, how are the tablet and keyboard commands passed to it? I would love to be pointed to more documentation on that and the linux stubdomain in general.

I appreciate the support! Please

Can you explain what you want to do, that isn’t already there by default? When you start a HVM, it gets emulated mouse (or rather tablet) and keyboard connected, that sends it input events when you interact with that HVM’s window. It isn’t your real full USB device. So, it sounds like exactly what you want, no?

Technically, qemu running in a stubdomain has built in GUI agent that shows just a single window (the one you see) using our GUI protocol.

I appreciate the fast reply. I want to quickly switch my keyboard and mouse between dom0 and the windows hvm. If i used a linux hvm, I can accomplish this using the input-proxy running from dom0 to the linux hvm to pass a keyboard and mouse. I guess I am curious why a custom libvirt xml for my windows hvm does not actually pass anything windows can see?

Also could I use the emulated mouse and keyboard without creating the VGA or gui agent window?

Edit: I also do not want to pass my usb keyboard/mouse to an untrusted domain and would prefer to just send inputs.

I should have given more context because there may be a solution I am not thinking about.

I have a windows hvm that has an nvidia gpu passed to it. The GPU, xen disk/network drivers are all working. Installing Qubes core agent in windows causes problems. I have read that some users can pass usb to the vm via a usb qube but it seemed not as stable. I also worry that virtually hot plugging a mouse and keyboard from the usb qube to windows and back will just cause issues. I do not need (or on the security side want) windows to have usb control over the mouse and keyboard. I only need the input sent to it.

I have the kind of input system that I want on a Debian HVM by doing the following:

I am using this project (GitHub - mciupak/libvirt-evdev: Libvirt evdev input service) to split my keyboard and mouse in dom0 (could be in a usb qube though) into essentially two devices each: . This lets me have a host and guest keyboard and the program lets me switch input between them.

Then I can use input-proxy-sender and receiver on the guest keyboard/mouse to pass the inputs to the Debian HVM. (GitHub - QubesOS/qubes-app-linux-input-proxy).

This seems to work very well. I wanted to create ability to switch between the gaming gpu enabled windows 10 and dom0 just like how in the KVM hypervisor you can switch control between host and guest. In KVM’s case it uses an evdev device tag in the libvirt XML. Xen does not have access to this: libvirt: Domain XML format. I do not need this to be built into Xen. I can use the libvirt-evdev program to do the split and switching for me but I cant seem to figure out how to do passthrough of a mouse and keyboard in Xen. In my search for this I cannot find any libvirt or xl config that demonstrates a keyboard/mouse passthrough. I have tried appending this libvirt config without any luck:

  <input type='passthrough' bus='virtio'>
    <source evdev='/dev/input/by-id/keyboard'/>
  </input>

I hope that makes more sense what I am aiming for. Thanks so much!

I see, so you can’t use the standard input integration because your VM doesn’t have normal VGA (and even if it would, its resolution, and screen location doesn’t match your actual video output through attached GPU.

The way you are trying to do it with libvirt xml won’t work, because it controls qemu that is running inside stubdomain, not dom0. So, it doesn’t have access to whatever /dev/input/* you point it at. It’s probably possible to add input proxy to the stubdomain to transfer input events there, and then attach that to qemu (via libvirt xml), but that will require some tinkering. If you want to try anyway, the stubdomain sources are here: GitHub - QubesOS/qubes-vmm-xen-stubdom-linux. The “full” variant already has qrexec (which is used for USB passthrough), so the missing piece would be “just” input proxy.

Technically, attaching USB keyboard/mouse directly would be simpler to accomplish (if you enable stubdom-qrexec feature using qvm-features tool), but as you correctly identified, it comes with exposing your USB devices to potential attack from that VM.

Unfortunately I don’t see any simple option that would just work and meet your requirements.

I appreciate you breaking that down for me. I will look into the full stubdomain as you have noted.

Is there a way to interact with the stubdomain so I can prototype things easily? Or somewhere else I can read up more about the Linux stub domain?

I have made progress in this matter but am stuck on the final part for a working prototype. I modified the package vmm-xen-stubdom-linux in Qubes builder to add uinput support:

Added the a file in linux/config/

CONFIG_INPUT=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=y

The program input-proxy-reciever should be compiled at this stage into the rootfs but I have just manually added it via:

See Neowutran’s gpu guide or gpu passthrough with 3.5GB issue

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

(ADD input-proxy-reciever to /bin)

This works and I have tested via running this command in dom0 (note -dm to access the stubdomain):

sudo qrexec-client -d gpu_hvm-dm -l 'input-proxy-sender /dev/input/by-id/mykeyboard' root:'input-proxy-reciever'

Using qrexec-client to pass commands to the stubdomain I can see that the uinput device is being created.

I am now stuck as I cannot run input-proxy-reciever before qemu starts since the vchan connection or socket isn’t established yet. It looks like in the init script for the stubdomain there is a qemu socket you could connect to. I tried finding how to connect to it from dom0 but cannot find any information. Is there a way to add a device while qemu is running?

If there is not a way to add an input device while its running then I will need to use something like libvirt-evdev to create a dummy uinput device for qemu to grab then later forward commands to it. This seems unnecessary to me though.

Thank you!

Update: uinput is working but an evdev or event device isn’t being created in /dev. I am not sure what is needed for that to be created.

Could you please provide information on how to modify the package vmm-xen-stubdom-linux in Qubes builder;
Also do you know if it’s possible to provide input to the stubdomain through sys-usb?

Have you checked if you compiled the stubdom kernel with CONFIG_INPUT_EVDEV enabled?

I’m stuck in the “attach that to qemu (via libvirt xml)” part, could you please give an example, when I tried using input type passtrough in libvirt as the example above it still says it’s missing the evdev path despite /dev/input/device0 being present in stubdom (I’ve created a dummy to be used until I run: sudo qrexec-client -d example-dm -l 'input-proxy-sender /dev/input/device*' root:'input-proxy-receiver --keyboard')

I am running this all from dom0 only because that is where my keyboard is attached, but the input proxy was designed to run from sys-usb. Ideally I would use sys-usb.

I modified the package but am only copying the rootfs to dom0. I followed the qubes builder page, had it download some sources so I didn’t have to build everything, and then ran make vmm-xen-stubdom-linux. I rebuilt it with CONFIG_INPUT_EVDEV and I do see the correct event device in /dev/input now. Let me know if you have more questions about this part, I can post what I did exactly.

How and at what time during the stub domain boot up are you creating the dummy device? I do not see how to call any qrexec commands before qemu has started in the stub domain. I imagine qemu is trying to use that dummy input before you have created it.

Sorry for taking so long to reply, I was hoping I would figure it out and than tell you the good news however that’s not the case.

The way I was creating a dummy device wasn’t the proper way of doing it as it was not a valid input, device I was just creating a file named event0 in /dev/input.

To use sys-usb we can just send the input to dom0 and use input-proxy-sender the same way.

Something that I’d like to try but I don’t know how to do is to create a valid dummy device so that qemu picks it up and then redirect the “real” input to the dummy

So far I’ve added the following to the rootfs init file:

evdev_args=$‘-object\ninput-linux,id=kbd1,evdev=/dev/input/event0,grab_all=on,repeat=on’

and $evdev_args to qemu just like $dm_args.

But since you can’t call qrexec commands before qemu starts; It won’t boot unless it gets a valid input device that’s why I’d like to create a fake input that receives the real input if that makes any sense (like a symlink).

I just realized you mentioned forwarding the input that’s exactly what I was trying to say have you made any progress in that?