Building a Board Support Package using Yocto

Posted on January 16, 2019
Tags: , ,
by Sanchayan Maity

This post assumes a basic working knowledge of Yocto/OpenEmbedded.

At my current workplace, for the project I am handling I am responsible for everything be it application, kernel configuration, custom drivers or existing driver enhancements or setting up the complete board support package for the system I am working on. I had worked on OpenEmbedded/Yocto at my previous company however, had no experience setting up a complete BSP. This meant having a custom meta layer for this project along with the required layers for Yocto to work and also the vendor specific layer which in this case was Xilinx, but, could be any for that matter. Accordingly, one would include meta-samsung, meta-ti or meta-intel and so on.

One of the first decisions I had to make was regarding the flashing process. We had an eMMC on-board and also a SD card. I could have my own script and using u-boot’s “mmc write” functions to format and write to the MMC. However, I decided to go the easier genimage approach. Yocto build system will build the relevant components and I could pack it in a single image using a configuration like below.

image boot.vfat {
    vfat {
        files = {
            "BOOT.bin",
            "emergency.ub",
            "image.ub",
            "system.dtb",
            "Image"
        }
    }
    size = 128M
}

image ged.img {
    hdimage {
    }

    partition boot {
        partition-type = 0xC
        bootable = "true"
        image = "boot.vfat"
    }

    partition rootfs {
        partition-type = 0x83
        image = "rootfs-zynqmp.ext4"
        size = 0
    }
}

This gives a single file with an img extension which I can use to flash the SD card or eMMC using the dd command.

Now, to use Yocto requires the use of several meta layers to be put together. One way is to use the repo tool which is also used in the Android world. However, I felt like I wanted something more along with Docker integration. Wondering if someone has already done this kind of work, I went searching on Google and came across yoe-distro.

yoe-distro provided a perfect template on which to base my work on. I only need integrate the layers I want and it could provide the rest. Docker integration was also included. Now, in case you are wondering why Docker is required, Yocto/ OpenEmbedded builds can be quite sensitive to host distribution setup. Also in my case, Xilinx’s support provided through Yocto would not build on Ubuntu 18.04 which I am running at work. Downgrading would result in waste of time and is not actually a solution. Even if worked on Ubuntu 18.04, future upgrades may result in the build not working.

Docker solves this problem by providing me a sandboxed environment, think of chroot or BSD jails simplified and on steroids. So I ended up integrating the vendor specific meta layers and added my own layer to provide customisations. Getting started with Yocto definitely requires putting in some effort, but, once you understand how to use it, adding customisations and stuff is lot easy or atleast has been quite easy for what I am doing.

For example, I definitely need to build the kernel from my own source tree, since I have some patches of my own and if nothing else my own custom device trees which enable support for custom Xilinx IP core drivers. One problem I had was we internally use stash and trying to even just clone the source tree from stash requires authentication. I was not sure how to do this in Yocto while without exposing my ssh keys or account information. yoe-distro’s setup helped here.

The linux source is added as a submodule just like the meta layers and then a custom recipe builds that.

SRC_URI = "git://${TOPDIR}/sources/ged4k-linux-kernel;protocol=file;branch=ged4k_v2018.2"
SRCREV = "fbec50e15d7b04f44690eb115d2cddd4423e0326"

SRC_URI_append += " file://defconfig \
                    file://fragment.cfg \
                    "

FILESEXTRAPATHS_prepend := "${THISDIR}/files:"

You can see the protocol specified is not git, but file. Another simpler example is u-boot, where I just needed to apply my own patch on top of Xilinx’s u-boot source and just the below in a recipe appending the original one.

FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
SRC_URI_append = "\
    file://0001-configs-xilinx_zynqmp-Config-SD-and-eMMC-boot-for-GE.patch \
"

I had to include a rl78flash program which was not provided by any of the meta layers in Yocto and even that was easy. I looked at how some other recipes were written and came up with this and it just worked.

SUMMARY = "RL78 Flash Tool"
DESCRIPTION = "Software to program RL78 microcontrollers via Serial Bootloader"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRCREV = "d0f5f05fbbce0a278658ca0caf67f293dcd26ee3"
SRC_URI = "git://github.com/msalau/rl78flash.git;protocol=https;branch=master \
          file://rl78flash.patch \
          "

S = "${WORKDIR}/git"

EXTRA_OEMAKE = "'CC=${CC}' 'CFLAGS=${CFLAGS}'"

do_compile() {
    oe_runmake
}

do_install() {
    install -d ${D}${bindir}
    install -m 0755 ${WORKDIR}/git/rl78flash ${D}${bindir}
}

Yocto by default uses sysvinit, however, I wanted systemd as init. Adding the below to local.conf will make systemd as init system.

DISTRO_FEATURES_append = " systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED += "sysvinit"
VIRTUAL-RUNTIME_init_manager = "systemd"
VIRTUAL-RUNTIME_initscripts = "systemd-compat-units"

It has been a lot fun learning how to put together a board support package and Yocto has given so much flexibility.