Comfortably virtualized control software

Comfortably virtualized control software

At Welcome Werkstatt, our open workshop in Hamburg, Barmbek, someone donated an old circuit board router to us. This is an LPKF S62. The software is technically still available for modern Windows systems, but the old uEye industrial camera is incompatible with any recent version of Windows and requires Windows XP.

From our perspective this is a fairly common occurance. The previous solution was to use some fairly old hardware that is supported by WinXP, but there are better options, like virtualization. In this case we're using a vanilla Ubuntu host.

The big picture

Virtualization has some unique advantages and some drawbacks:

  • WinXP has not seen updates for quite some time, hence the guest should run with the highest amount of network isolation possible. At the same time it's desirable that the host has continuous network access and consequently a supported software base.
  • CAM and board layout software need to run at reasonable speed.
  • Both the uEye camera and the Prolific serial adapter need to be supported. These are USB2 devices.
  • We need a reasonable way to move data in and out of the guest OS. A USB stick will not work.

The diagram below gives you some idea of how everything fits together.

The gist:

  • We'll share files via Samba
  • We'll use virt-viewer/Spice for KVM
  • We implement USB hotplug for every device that connects to a designated USB hub

This will give us a host system with all the current Linux software you would expect and at the same time seamless access to the old machine control software.

Let's go.

General preparation

I used a vanilla Ubuntu 24.4 with literally zero customization, and applied the most recent patches.

You need to install a couple of things:

VM preparation & networking

First we'll let libvirt create a so called routed network. All the required information can be found over on the virsh site (e.g. https://wiki.libvirt.org/VirtualNetworking.html#xml-format). We'll create a routed network on virbr1 with a private IP space, that is unused in my bubble, and after that we'll enable Ubuntus UFW firewall.

cat > lpkf-net.xml <<EOF
<network>
 <name>lpkf-net</name>
 <bridge name='virbr1' stp='on' delay='0' />
 <ip address="192.168.110.1" netmask="255.255.255.0">
 <dhcp>
  <range start="192.168.110.100" end="192.168.110.254" />
 </dhcp>
</ip>
EOF

virsh net-create lpkf-net.xml

sudo ufw enable
sudo ufw status verbose

Technically this network is called a "routed" network in the virsh docs, but it is not routed. The reason is given by the last command and its output: UFW blocks all communication by default, routed or not (with the exception of DNS).

Installing WinXP is not horribly complex. You open virt-manager, click the button for creating a new VM and point it to the WinXP CD image and it will automatically mount it and start the installer. I will name this VM lpkf-life-extension. I create a VM with 20GB storage and 2GB of RAM. When you create the VM, make sure that the network is not set to default, but to lpkf-net.

Now, the next step will involve adding more software to the guest, but first, let's create some easy way to move data around. For this one we'll use a Samba with horrible security configuration. That's another reason for the firewall config we did previously.

Install Samba as per Ubuntus docs (https://ubuntu.com/tutorials/install-and-configure-samba#1-overview). I changed the share config from the docs to the following, this should be placed at the end of the samba config (/etc/samba/smb.conf).

[lpkf-inter]
    comment = LPKF Interchange
    path = /home/mark/lpkf-inter
    read only = no
    browsable = yes
  

Now, this will not be accessible by WinXP, because this particular guest OS expects an early version of CIFS and working NTLM auth. Both of these are deeply flawed and should not leave the virtual network on this machine. Make the following additions to the [global] section of the same file and restart the Samba server.

server min protocol = NT1
ntlm auth = yes

Our firewall is still blocking all requests to the Samba server, so we need to open the NetBIOS ports for incoming traffic from the LPKF network, and this network only.

sudo ufw allow from 192.168.110.0/24 to 192.168.110.1 port 137
sudo ufw allow from 192.168.110.0/24 to 192.168.110.1 port 138
sudo ufw allow from 192.168.110.0/24 to 192.168.110.1 port 139

sudo ufw status verbose

At this point you can mount the interchange share from WinXP. So open a command prompt on WinXP and enter the following command to mount the network share.

net use z: \\192.168.110.1\lpkf-inter

Add some Spice

The Spice tools support convenient desktop integration, aka KVM. The Spice installer contains:

  • QXL video drivers, these will make the screen resize dynamically to whatever your virt-viewer window allows
  • The Spice agent proper for some convenience.

Get the Spice tools over at the project web site (https://www.spice-space.org/download.html), copy them to the lpkf-inter directory, open the guest OS in virt-manager and install them in the local machine.

Before you reboot WinXP, let's check two things. In the virt-manager config pane, check that you have a spice channel for KVM. The hardware type should be "Channel" in the overview pane. After that check that the network adapter type is set to "virtio" (not "e1000") and that the Graphics card type is set to "QXL".

After you reboot WinXP, you will be able to fire up virt-viewer and you'll have a reasonably working virtualization experience.

Virtual USB hotplug sans suck

I'll remind you here of our setting: We have a CNC router standing in our workshop, this is a small to medium sized machine tool. For most uses you will want to be able to attach some device to the guest OS and "just have it work". People should not care much about the underlying host OS, if the only thing they want to do is route some boards. With the Spice integration and seemless file sharing we've achieved a good level of convenience, now let's match that for the experience of "plugging in a USB device and have it show up directly on the guest OS". This affects both our router hardware interface and ad-hoc connections, say to a USB stick.

The way we will achieve this, is via some very arcane hardware device, called a USB hub. The machine in place already has a (fairly old) USB hub attached, and all parts for machine control are attached here. The USB bus is a structured bus and it's fairly easy to map out the topology on Linux systems. From the host perspective the USB topology looks vaguely like the following diagram.

Sample USB Topology

What we need is a small script that does the following:

  • The script runs for every attached USB device
  • It will inspect the USB topology until it arrives at a root controller
  • If the device that was just attached is situated below a designated hub, the device should be hot-plugged into the VM and the host OS should not bother with the device at all

In other words: We look up the vendor ID and device ID from our hub and whenever someone attaches something to this hub, the device gets passed straight through to the guest OS.

The following is a small script to accomplish just this within the Linux udev framework. If this script criteria are met and it has plugged in the device to the VM, it reports success and udev resolution stops at this point. If the script criteria don't match or it fails to plug in the device, the rule resolution will continue and the host OS will do its thing, e.g. mount a USB stick.

This is a modified version of this piece: https://github.com/olavmrk/usb-libvirt-hotplug My changes add topology awareness, the original script would plug in just a specific device at a specific device path. Since device pathes are somewhat unpredictable on Linux, the original script is not ideal either way.

If you adapt the script in question, make sure to change the vendor id and product id to the ids of your hub or controller. See top of script.

In addition to place a udev rule at the right place and reload the udev system. On Ubuntu the right place is /dev/udev/rules.d and the command to reload the rules is sudo udevadm control --reload-rules && sudo udevadm trigger.

For the rule, I used the following snippet. The RUN command in the rule needs to point to an executable version of the script above and include the VMs name as the first argument.

SUBSYSTEM=="usb",DEVPATH=="*",RUN+="/opt/usb-libvirt-hotplug/usb-libvirt-hotplug.sh lpkf-life-extension"

Summary

Things that need to be checked again:

  • My tests showed that the VM had almost no connection to the internet. The VM can resolve DNS and run DHCP (the latter only locally), everything else should be disallowed. As part of this the actual iptables entries that ufw generates need to be cross checked (ufw show raw).
  • WinXP might need some weird activation after 30 days.
  • virsh might want to increment port numbers for all attached devices. According to the internet the QEMU EHCI implementation has 4 ports and when virsh arrives at number 4, it will add an emulated USB UHCI controller (USB 1.1) and attach the device to the new controller.
  • You might want to mount USB mass storage into lpkf-inter even if they are supported by WinXP. According to my research, WinXP handles FAT32 fairly well, but nothing that came later.

Drawbacks observed:

  • If you start the guest OS after the USB hub has been attached, the devices will not be connected to the guest OS. Simply unplugging the hub and plugging it back in will fix this.
  • The device topology in the guest OS may be different at every attachment. The reason for this is the fairly unpredictable order in which devices get attached to the guest VM. This can mean that the COM port numbers of the Prolific converter change and it can also mean that you will be prompted to re-install the uEye drivers on boot. This is a quirk with WinXP USB. The fix for this is fairly simple and can be done in the script.

This session was primarily to verify that this way of virtualizing the control software is sound. And I think this is the case.

Installing the CAD/CAM software is left as an exercise to the reader.

Rejected alternatives

Some things that don't work:

  • virtio filesystem: This only works from Windows 8 onwards. This is the best way to achieve a good level of isolation of the Windows guest. Guest OS support for the virtio filesystem drivers means that you can run your guest OS without an attached network card, without any concern for accidental network connections.
  • spice shared folders: The most recent webdav proxy version (2.4) for this does not work on WinXP any longer. The previous to last version (2.2) does work. The startup of the proxy does however fail and will not recover, when the guest OS boots and there is no Spice viewer attached to the VM. This leads to a stream of errors in the event log and requires a normal user to restart the Spice webdav service in the guest OS, whenever they connect to the guest OS.
  • attaching a usb hub directly: Many modern virtualization technologies support direct mappings of hardware devices into the guest OS. This can be done with PCI devices (graphics cards &c). For our use case, it might have been possible to map a USB root controller into the guest. This doesn't work, because WinXP is limited to the USB EHCI controller standard and any modern system will provide an xHCI controller in Hardware. The former one was part of USB 2.0, while the latter was introduced with the advent of USB 3. Under the hood, our solution uses an emulated EHCI controller and the USB payload is forwarded from the host USB stack. It's still efficient and adequate for our use case.
Follow me on Mastodon!