Go back
teaching QEMU some new tricks 2021-05-07

I have a Pinebook Pro - it's been a good ARM laptop, but sometimes there's just that bit of x86 software you want. There are a few different solutions to this, but the one I'm looking at is qemu's usermode emulation. QEMU has support for running Linux binaries on a different architecture by emulating only the binary, and passing through/translating the syscalls.

It has fairly good coverage of all the existing syscalls, but doesn't quite have all of the ioctls needed to run graphical applications - I had to add some missing DRM ioctls, as well as the 7 Panfrost-specific ones.

The process to add a new ioctl is fairly easy if it only takes a "plain" struct (ie, no pointers). Under the linux-user folder in the source tree there are 4 files of interest - syscall.c, syscall_defs.h, syscall_types.h and ioctls.h.

ioctls.h holds all of the ioctl definitions, which are macros that specify the ioctl ID and its argument type (typically a pointer to a struct).

syscall_defs.h holds the target ioctl number definitions - these need to be copied out of the kernel so they match. It also holds the definitions of all target_ prefixed structs, which are defined using special abi_ types. These allow QEMU to control the alignment of all members to ensure the target_ prefixed structs match the layout as it is on the target architecture exactly.

Next, syscall_types.h holds all the structure definitions again, but with a special STRUCT macro - this is how the automatic marshalling knows about the struct layout.

If your structure is simple, you can just define it as an IOCTL in ioctls.h, define the structure in syscall_defs.h / syscall_types.h and QEMU will do the translation automatically! If you have any pointers to user data, you need to define an IOCTL_SPECIAL with your own handler function that lives in syscall.c. This handler will then have to copy the user data from the host struct to the target struct.

If there's one thing I have to say about the QEMU codebase, it's .. very C. Lots of macros everywhere.

Using the incredibly accurate benchmarking method of "run supertuxkart and look at the FPS", there's clearly a little overhead, but not a huge amount:

Native: ~ 14 fps

SuperTuxKart with a FPS overlay

x86: ~ 11 fps

SuperTuxKart with a FPS overlay

As you can see, the Pinebook Pro's GPU isn't very good - maybe this would be more useful on a more powerful Mali GPU. It was fun to do, anyway.

The source is on my GitHub, in the panfrost branch.