So, today I had a problem… I needed to solve a race condition in a test case for my workplace’s WideSky system. The test case was meant to ensure that, if the AMQP broker crashed or was restarted, it would re-connect and resume operations as quickly as possible.
On my desktop (an 8-core AMD Rysen 7), the test case always passed. On the CI server (a VM running on a dual-core Core i3), it failed. I figured the desktop here was running too quickly for me to see the problem. I needed a machine that ran more like the CI server to see the problem.
Looking around, I couldn’t see any way to reliably slow down QEMU, KVM or VirtualBox… but I do remember one old project from the mid-late 90s that could: Bochs.
Turns out, far from what it could do back in 1998 when it was strictly a 386 emulator (and a slow one at that!) it now has AMD64 emulation capabilities. Thus, I can run the software stack inside this VM, and have it throttle the CPU speed down so that hopefully, the problem arises. The first problem I needed to solve was trying to get the network running. We have a PXE boot server which can serve up Ubuntu, no problem. I just needed to bridge the Bochs VM onto the network somehow.
I already have bridge interfaces configured on my two physical network interfaces, and these work great with KVM. Sadly, Bochs is rather primitive in what it supports…
tap-mode networking just did not work, it complained that tap0 was not “running” even if created beforehand by iproute2, but I did find I could bind it directly to one of the enslaved network interfaces (enp36s0.200; yes, a VLAN interface).
e1000 worked for network booting, but then Ubuntu couldn’t retrieve an IP address for whatever reason. ne2k is working fine, and presently, I have the VM installing. To make it network bootable, you need a boot ROM image, which you can download from the iPXE rom-o-matic service. The magic PCI IDs you need are 10ec 8029 for ne2k, or (if it gets fixed) 8086 10de for e1000.
The following is my Bochs config file:
# configuration file generated by Bochs plugin_ctrl: unmapped=1, biosdev=1, speaker=1, extfpuirq=1, parallel=1, serial=1, gameport=1, ne2k=1 config_interface: textconfig display_library: x debug: action=report memory: host=2048, guest=2048 romimage: file="/usr/share/bochs/BIOS-bochs-latest", address=0x0, options=none vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest" boot: disk, network floppy_bootsig_check: disabled=0 # no floppya # no floppyb ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 ata0-master: type=disk, path="/tmp/wstest.raw", mode=flat, cylinders=0, heads=0, spt=0, model="Generic 1234", biosdetect=auto, translation=auto ata0-slave: type=none ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15 ata1-master: type=none ata1-slave: type=none ata2: enabled=0 ata3: enabled=0 optromimage1: file=none optromimage2: file=none optromimage3: file=none optromimage4: file=none optramimage1: file=none optramimage2: file=none optramimage3: file=none optramimage4: file=none pci: enabled=1, chipset=i440fx, slot1=ne2k, slot2=cirrus vga: extension=cirrus, update_freq=5, realtime=1 cpu: count=1:1:1, ips=40000000, quantum=16, model=p4_prescott_celeron_336, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0 print_timestamps: enabled=0 port_e9_hack: enabled=0 private_colormap: enabled=0 clock: sync=none, time0=local, rtc_sync=0 # no cmosimage # no loader log: - logprefix: %t%e%d debug: action=ignore info: action=report error: action=report panic: action=ask keyboard: type=mf, serial_delay=250, paste_delay=100000, user_shortcut=none mouse: type=ps2, enabled=0, toggle=ctrl+mbutton speaker: enabled=1, mode=system parport1: enabled=1, file=none parport2: enabled=0 com1: enabled=1, mode=null com2: enabled=0 com3: enabled=0 com4: enabled=0 ne2k: enabled=1, mac=fe:fd:de:ad:be:ef, ethmod=linux, ethdev=enp36s0.200, script=/bin/true, bootrom="/tmp/10ec8029.rom"
Create your hard drive image using qemu-img, then run bochs -f yourfile.cfg and it should, hopefully, work.