Let’s hack QEMU. That is what I was saying a few days ago. I always wanted to explore QEMU and the whole virtualization stack, but never got around to it, and now that I had the time to do so I gave it a try.
It was pretty rough. I wasn’t able to find any good tutorials on how to add custom virtualized devices to QEMU, not even ISA or PCI, so I decided that I’ll be writing up a blog post about the creation of a very simple PCI device in QEMU. Here is a list of what we will cover during this post series:
- A PCI device in QEMU, from which you can read byte-by-byte a string (e.g. “Hello, world!”)
- A Linux kernel module to read it and possibly write to it!
… and a list of what we will not cover:
- Getting the source code of Linux and QEMU
- Configuring and building them
Let us start. We will create a new file called pci-tutorial.c in the QEMU sources in folder hw/char/ since we are creating a character device. Here is a gist showing what you should put inside the file: pci-tutorial.c
I know that is a bit too much for a single shot, so let me tell you how it all works. We’ll use a technique I call ‘reverse-read’ for understanding the code. So, looking at the bottom of the code you can see a macro called
'type_init' with the sole parameter being the init function of the file. This is called 'type_init' since QEMU creates an object orientated interface, and we need to register a type. The macro invokes 'pci_tut_register_types' which will register the type which is defined by the struct 'pci_tut_info'. Here we have a field called name and that is what defines the name we will pass to QEMU's '-device' flag.
Function ‘pci_tutdev_class_init’ is what you can call the class static initializer. This is the function which sets up the static variables of the class. Since we specified we are creating a PCI subclass in the struct ‘pci_tut_info’, we are being passed a PCIDeviceClass in which we can specify PCI-specific information (e.g. vendor, device IDs). Note, we are also specifying an init function called ‘pci_tutdev_init’ which can be thought of like it was the constructor for the pci-tutdev objects! Likewise there exists an uninit function which can be the destructor if you’d like.
If this was a bit rough here is an image showing the flow:
Now, we need to add this file to the Makefile.objs in order for the QEMU build system to build this and link it to the resulting executable as well:
Add the following line:
obj-$(CONFIG_PCI) += pci-tutorial.o
What this says is pretty simple, if the configuration option called CONFIG_PCI is enabled then the pci-tutorial.c file will be built and linked into the QEMU emulator.
Now, if you build QEMU and pass ‘-device pci-tutdev’ to it you will most likely see something like this provided you start a Linux kernel inside it:
Now, there is nothing fun in that until you write a simple module for the Linux Kernel to pick it up. In the next part of this series we’ll cover how you can write a simple Linux kernel module that will pick up this device.
Thanks for reading!
Fedora Project Hungary