Integrating support for custom QEMU peripherals in Yocto

Integrating support for custom QEMU peripherals in Yocto

In the previous posts a custom memory-mapped peripheral was introduced, together with the Linux device driver for it and a userspace application which can be used to interact with it. Besides the memory-mapped peripheral, a custom-i2c-peripheral was also described.

The functionality and integration of the custom components were demonstrated using a SD card image with Ubuntu root filesystem.


In this post the steps for integrating the custom components into the Yocto build environment will be described. The following items will be covered

NOTE: The Yocto version has been bumped from kirkstone to scarthgap (the latest LTS version) compared to the original Yocto post.

Related to the change of Yocto version, the command to initialize the repo environment has changed to

repo init -u https://github.com/straxy/qemu-sunxi-yocto -m default.xml -b scarthgap

From the end-user standpoint, there are no other changes needed, and steps from the previous post can be used as is.

From the developer standpoint, the changes were made in several locations in order to use scarthgap

  • on the new scarthgap branch in qemu-sunxi-yocto
    • the manifest file has been updated to use scarthgap for all layers
    • meta-arm (new dependency of meta-sunxi) has been added to the manifest
    • bblayers template has new meta-arm and meta-arm-toolchain layers
  • on the new scarthgap branch in meta-mistra
    • the layer.conf has been updated to be compatible with scarthgap
    • the systemd network interface file has been changed to end0 from eth0

Creating a new Yocto MACHINE description

The MACHINE description in Yocto is used to configure different aspects of BSP (Board Support Package) for a certain board. Some of the items that can be configured are

  • architecture compilation options,
  • preferences for virtual packages provided by different modules (bootloader, kernel, etc.),
  • device tree file to be used,
  • default image formats that will be built,
  • SD card layout,
  • and many others.

The information about the MACHINE that is selected can also influence the way other packages are configured or handled during different steps of the build process, by using the overrides mechanism.

For instance, in order to add extra file for package mypackage only for MACHINE=mymachine, a SRC_URI would be added to mypackage.bb with SRC_URI:append:mymachine="extrafile".

Since some of the configuration should be shared between the different boards (MACHINEs), then the MACHINEOVERRIDES is used to define shared overrides. For example, all sunxi machines have sunxi added to the MACHINEOVERRIDES variable, so every override defined for sunxi applies to all of those MACHINEs.

The MACHINE descriptions are stored in <machine_name>.conf files in the conf/machine directory of a layer, and <machine_name> is automatically added to the MACHINEOVERRIDES variable.


In the original Yocto post the MACHINE=cubieboard description was used. That description comes from the meta-sunxi layer and supports the original Cubieboard.

However, since we have modified the memory map of the Allwinner-A10 and added the new I2C component, it would be good to create a new Yocto MACHINE description, so Cubieboard MACHINE can still be used with the original board and QEMU implementation.


A new MACHINE description in Yocto is created by adding a new cubieboard-ng.conf file in the conf/machine directory

require conf/machine/include/sun4i.inc

MACHINEOVERRIDES =. "cubieboard:"

KERNEL_DEVICETREE = "sun4i-a10-cubieboard.dtb"
UBOOT_MACHINE = "Cubieboard_config"
SUNXI_FEX_FILE = "sys_config/a10/cubieboard.fex"

The description is based on the cubieboard.conf description for Cubieboard, but also updates the MACHINEOVERRIDES so every configuration that is used for the “original” Cubieboard also applies to this one. The items that will be specific for our custom board will be overridden with cubieboard-ng.

Updating Linux kernel recipes

The kernel configuration for the modified Cubieboard should include the description of the memory-mapped peripheral in the Device Tree description, as well as the device driver for that peripheral.

Device tree patch

The Device tree should be updated for the memory-mapped peripheral, so it will be detected at boot time.

Since the device tree exists in the Linux kernel repository, a patch has been made to add the changes to it for the memory-mapped peripheral. The changes are the ones described in the device driver post

The patch itself is placed inside the recipes-kernel/linux/linux-mainline directory in the meta-mistra layer, and integrated into Yocto using the SRC_URI variable in the linux-mainline_%.bbappend recipe

SRC_URI:append:cubieboard-ng = "\
    file://sun4i-a10.dtsi-Update-for-custom-memory-mapped-device.patch \
"

By using the cubieboard-ng override, the patch will only be applied when the cubieboard-ng machine is used.

Device driver recipe

In order to add the mmsens driver to Yocto a new recipe has to be created. The template (skeleton) exists at poky/meta-skeleton/recipes-kernel/hello-mod.

The recipe for the module should look like

SUMMARY = "Memory-mapped QEMU sensor driver"
DESCRIPTION = "${SUMMARY}"
LICENSE = "GPL-2.0-only"
LIC_FILES_CHKSUM = "file://COPYING;md5=12f884d2ae1ff87c09e5b7ccc2c4ca7e"

inherit module

SRC_URI = "git://github.com/straxy/mmsens-drv.git;protocol=https;branch=main"
SRCREV = "${AUTOREV}"

S = "${WORKDIR}/git"

# The inherit of module.bbclass will automatically name module packages with
# "kernel-module-" prefix as required by the oe-core build environment.

RPROVIDES:${PN} += "kernel-module-mmsens-drv"

This module also needs to be added to the image recipe so it will be included in the output root filesystem image

IMAGE_INSTALL:append:cubieboard-ng = "\
    kernel-module-mmsens-drv \
"

Adding mmsens-app recipe

Similarly to the kernel module, the mmsens application can be added to the Yocto build system by adding a new recipe for it. The recipe is located in recipes-apps/mmsens-app/mmsens-app_1.0.bb.

The recipe inherits cmake which indicates to bitbake that CMake is used as the build system. That way the bitbake will use the standard CMake build steps to make the executable and copy it to the final root filesystem.

DESCRIPTION = "Memory-mapped sensor userspace application"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=e983ef1fada174da9affb3753a18936f"

SRC_URI = "git://github.com/straxy/mmsens-app.git;protocol=https;branch=main"
SRCREV = "${AUTOREV}"

S = "${WORKDIR}/git"

inherit cmake

The only other thing needed is to add mmsens-app to IMAGE_INSTALL variable in the image recipe, and the application will be built and included in the final output image.

Adding i2csens-app recipe

The steps for adding the i2csens application are the same as adding the mmsens application to the Yocto build system.

The recipe file is located in recipes-apps/i2csens-app/i2csens-app_1.0.bb, and the biggest difference in the recipe content is the git repository address.

The recipe also has to be added to the IMAGE_INSTALL variable, so after adding all three described recipes, the final change to the mistra-image.bb should look like

IMAGE_INSTALL:append:cubieboard-ng = "\
    kernel-module-mmsens-drv \
    mmsens-app \
    i2csens-app \
"

Building and testing image

Building a Yocto image with the new MACHINE follows the same process as in the original Yocto post. The only difference is that the new MACHINE=cubieboard-ng is used, so the build invocation should be

source setup-environment build_fb
DISTRO=mistra-framebuffer MACHINE=cubieboard-ng bitbake mistra-image

One big difference is that with the scarthgap release the .sunxi-sdimg is not built anymore, and instead of it the .wic.gz is build by default.

In order to make an sd.img which can be used with the QEMU, the following steps should be taken (NOTE: assumed that qemu-img is in the $PATH and that commands are executed from within the build_fb directory)

qemu-img create sd.img 1G
gunzip -c ${PWD}/tmp/deploy/images/cubieboard-ng/mistra-image-cubieboard-ng.wic.gz | dd of=sd.img bs=1M iflag=fullblock oflag=direct conv=fsync
qemu-img resize sd.img 1G

After the SD card is prepared, the QEMU can be started using

# Run QEMU with SD card and networking
qemu-system-arm -M cubieboard -m 1G \
                  -drive file=sd.img,format=raw,if=sd \
                  -net nic -net tap,ifname=qemu-tap0,script=no \
                  -nographic

The first thing that can be noticed is that the kernel module is loaded automatically

[   27.406924] mmsensdrv: loading out-of-tree module taints kernel.
[   27.499134] Hello, world!

Both mmsens-app and i2csens-app are present in the $PATH, so they can be invoked just by

mmsens-app /sys/class/mmsens/mmsens0
Hello World!
[  129.749436] Interrupt received
Initial 0001
[  130.749875] Interrupt received
0002
[  131.748683] Interrupt received
0003
[  132.748993] Interrupt received
0004
^C[  133.749004] Interrupt received
0005

and

i2csens-app /dev/i2c-1
Measured 25
Measured 25
Measured 15
Measured 25
Measured 23.5
^CMeasured 24

Summary

In this blog post a new Yocto MACHINE has been added for the customized Cubieboard. A new MACHINE helps isolate the changes related only to the customized Cubieboard, so the same environment can still be used to build images for the original Cubieboard.

The recipes for the driver and userspace applications for the custom QEMU peripherals are also added, so they will be included with every image built for the new MACHINE.

One more thing that is covered is the steps that were taken to bump the Yocto version that is used to the latest LTS version, scarthgap. It was easy to do the migration since the meta-mistra layer did not have many contents or dependencies, but in real applications the changes might also be needed for the kernel and bootloader support, since with the new Yocto version the U-Boot and Linux versions have been updated.

Subscribe
If you would like to get information as soon as new content is published, please subscribe to the "MistraSolutions newsletter".