NetBSD Qemu Edu - hpaluch/hpaluch.github.io GitHub Wiki

NetBSD driver for QEMU EDU

Goal: try to write both kernel (and partially user-space) driver for QEMU EDU Device (educational virtual device with emulated Interrupt and DMA) for NetBSD Guest.

  • Host: openSUSE LEAP 15.6 with LibVirt + KVM (qemu 8.2.10).
  • Guest: NetBSD 11 RC1 (snapshot 20260206082425Z).

On host we have to add -device edu to enable virtual EDU Device to NetBSD guest.

  • in case of LibVirt we have to follow: https://libvirt.org/drvqemu.html
  • use virsh edit VM_NAME and:
  • add on top namespace xmlns:qemu: <domain type='qemu' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
  • and at the bottom (right before closing element </domain>):
    <qemu:commandline>
      <qemu:arg value='-device'/>
      <qemu:arg value='edu,addr=0xa'/>
    </qemu:commandline>
  • note: addr=0xa is address override to avoid conflict on QEMU start (LibVirt will allocate some devices in lower addresses preventing QEMU to start)

Above configuration will create new PCI device with:

  • Vendor ID: 0x1234 (Yes, that "round" number)
  • Device ID: 0x11e8

Once you boot NetBSD Guest VM, you should see new PCI device:

(run as root):

$ dmesg -t | grep 1234

vendor 1234 product 11e8 (prehistoric, subclass 0xff, revision 0x10) at pci0 dev 10 function 0 not configured

(Full device list with kernel drivers)

$ pcictl pci0 list -N

000:00:0: Intel 82441FX (PMC) PCI and Memory Controller (host bridge, revision 0x02) [pchb0]
000:01:0: Intel 82371SB (PIIX3) PCI-ISA Bridge (ISA bridge) [pcib0]
000:01:1: Intel 82371SB (PIIX3) IDE Interface (IDE mass storage, interface 0x80) [piixide0]
000:01:3: Intel 82371AB (PIIX4) Power Management Controller (miscellaneous bridge, revision 0x03) [piixpm0]
000:02:0: Red Hat QXL Video (VGA display, revision 0x05) [vga0]
000:03:0: Qumranet Virtio Network (ethernet network) [virtio0]
000:04:0: Intel 82801FB/FR High Definition Audio Controller (mixed mode multimedia, HD Audio 1.0, revision 0x01) [hdaudio0]
000:05:0: Intel 82801I USB UHCI Controller (USB serial bus, UHCI, revision 0x03) [uhci0]
000:05:1: Intel 82801I USB UHCI Controller (USB serial bus, UHCI, revision 0x03) [uhci1]
000:05:2: Intel 82801I USB UHCI Controller (USB serial bus, UHCI, revision 0x03) [uhci2]
000:05:7: Intel 82801I USB EHCI Controller (USB serial bus, EHCI, revision 0x03) [ehci0]
000:06:0: Intel 82801I AHCI SATA Controller w/ 6 ports (SATA mass storage, AHCI 1.0, revision 0x02) [ahcisata0]
000:07:0: Qumranet Virtio Console (miscellaneous communications) [virtio1]
000:08:0: Qumranet Virtio Storage (SCSI mass storage, vendor-specific) [virtio2]
000:09:0: Qumranet Virtio Memory Balloon (prehistoric, subclass 0xff) [virtio3]
000:10:0: vendor 1234 product 11e8 (prehistoric, subclass 0xff, revision 0x10)

(Huh - we are lucky - with 0xa address (10 decimal) - first free address in PCI space...)

$ pcictl pci0 list | grep 1234

000:10:0: vendor 1234 product 11e8 (prehistoric, subclass 0xff, revision 0x10)

(dump most useful information:)

$ pcictl pci0 dump -d 10 | sed -n '/register at 0x10/,/base/p;/ID:/p;/Command register:/,/Interrupt disable:/p'
    Vendor ID: 0x1234
    Device ID: 0x11e8
    Command register: 0x0103
      I/O space accesses: on
      Memory space accesses: on
      Bus mastering: off
      Special cycles: off
      MWI transactions: off
      Palette snooping: off
      Parity error checking: off
      Address/data stepping: off
      System error (SERR): on
      Fast back-to-back transactions: off
      Interrupt disable: off
    Subclass ID: 0xff
    Revision ID: 0x10
    Base address register at 0x10
      type: 32-bit nonprefetchable memory
      base: 0xfc000000
    Subsystem vendor ID: 0x1af4
    Subsystem ID: 0x1100

Note:

  • we see base address of BAR0 register 0xfc000000, but not its size (we will need it for memory map)
  • in qemu source we can found:
     memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu,
                      "edu-mmio", 1 * MiB);
  • so it should be 1 MiB big (0x100000)
  • to determine BAR size we have to use this clumsy process (TODO):
    1. Disable IO and MEM access (Command register)
    2. Save BAR register value
    3. Write all 1s to BAR
    4. Readback - determine size
    5. Restore BAR value
    6. Enable IO and MEM access (Command register)

Important resources

To access PCI from user-space - most useful seems to be:

  • /usr/xsrc/external/mit/libpciaccess/dist/src/netbsd_pci.c
  • for kernel level access there are many other examples - best is to search for pci_mapreg_map
⚠️ **GitHub.com Fallback** ⚠️