mandag den 17. november 2025

Working with Yocto: Tailoring an image

Tailoring what is included in an image

 
The standard image that is part of the OpenSTLinux distribution includes a lot of things that would not be needed in a production environment - most notably the demo applications. Assuming you built the image, it is possible to list the  packages installed with the image by opening
 
tmp-glibc/deploy/images/stm32mp15-disco/st-image-weston-openstlinux-weston-stm32mp15-disco.rootfs.manifest
 
As it can be seen a lot of packages are installed.  Most of them are pulled in by package groups. Those are the ones that start with packagegroup-. In the following we will build two images - one for deployment and one for development. The development image will be identical to the st-image-weston except we will remove demos and gstreamer. The deployment image will not have debug and ssh access. 
 
Having a split like this allows including some unsafe practices in the development image (such as debug access, hardcoded credentials or SSH keys) that makes development easier. 
 
To get started we do:
 
cd $BUILDDIR
bitbake-layers create-layer ../layers/meta-bdp
bitbake-layers add-layer ../layers/meta-bdp 
mkdir -p ../layers/meta-bdp/recipes-core/images/ 
 
to add a new layer to the tree. 
 
Now  create ../layers/meta-bdp/recipes-core/images/bdp-image-deploy.bb and add the following contents:
 
 
SUMMARY = "Custom image based on st-image-weston"
 
require recipes-st/images/st-image-weston.bb
 
# Remove unwanted package groups
IMAGE_INSTALL:remove = " \
    packagegroup-st-demo \
    packagegroup-gstreamer1-0 \
    packagegroup-core-eclipse-debug \
    packagegroup-core-ssh-dropbear \
 
Now we can build this image:
 
bitbake bdp-image-deploy 

Since this image is based on the image we already built, it will not take more than a few minutes to build. 
 
Now we can program the new image to the SD card:
 
cd  tmp-glibc/deploy/images/stm32mp15-disco
 
In there we now have  a directory flashlayout_bdp-image-deploy that contains the files necessary to write the SD card, so we insert the SD card in the host machine, unmount all partitions and do:
 
scripts/create_sdcard_from_flashlayout.sh \
flashlayout_bdp-image-deploy/opteemin/FlashLayout_sdcard_stm32mp157f-dk2-opteemin.tsv
 
sudo dd if=FlashLayout_sdcard_stm32mp157f-dk2-opteemin.raw of=/dev/mmcblk0 bs=8M conv=fdatasync status=progress
 
Now we can boot our new image. As it can be seen the demos no longer are available. 
 
Now it is time to create the development image. We will 
reate ../layers/meta-bdp/recipes-core/images/bdp-image-devel.bb and add the following contents:
 
 
SUMMARY = "Custom image based on bdp-image-deploy"
 
require bdp-image-deploy.bb
 
# Install wanted packages
IMAGE_INSTALL:append = " \
    packagegroup-core-eclipse-debug \
    packagegroup-core-ssh-dropbear \
    nano \ 
    wifi-profiles \ 
 
 This is enabling debug and SSH as well as nano as editor (as I am not a big vi fan). Furthermore we pull in wifi-profiles that is a custom recipe that will setup wifi and install a public key to use for login over SSH.
 

Creating the recipe 

First we need to make sure to have an identity to use for SSH.  On your host:
 
ls ~/.ssh 
 
If you have a file called id_ed25519.pub you are good to go. If not you should:
 
ssh-keygen -t ed25519 -C "your_email@example.com"
 
to generate a keypair for use with the board. 
 
Now we add a new directory for the files that we need to install in the image. On your host:
 
cd $BUILDDIR
cd ../layers/meta-bdp
mkdir -p recipes-connectivity/wifi-profiles/files
cd recipes-connectivity/wifi-profiles/files

We then generate  the ssh authorized keys:

cp ~/.ssh/id_ed25519.pub authorized_keys

and we generate the service file for wifi:
 
cat << EOF > 51-wireless.network
[Match]
Name=wlan0
[Network]
DHCP=ipv4
EOF
 
... and the wlan0 setup for wpa-supplicant:
 
cat << EOF > wpa_supplicant-wlan0.conf
ctrl_interface=/var/run/wpa_supplicant
eapol_version=1
ap_scan=1
fast_reauth=1

EOF
wpa_passphrase SSID PASSPHRASE >> wpa_supplicant-wlan0.conf
 
In the last line you need to replace SSID and PASSPHRASE with the SSID and password of the  network you connect to. 
 
Now it is time to build the recipe. It is put in ../wifi-profiles.bb and looks like this: 
 
SUMMARY = "WiFi configuration"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRC_URI = "\
    file://wpa_supplicant-wlan0.conf \
    file://51-wireless.network \
    file://authorized_keys \
"

S = "${WORKDIR}"

do_install() {
    # ---- wireless network definition ----
    install -d ${D}/usr/lib/systemd/network
    install -m 0644 ${WORKDIR}/51-wireless.network ${D}/usr/lib/systemd/network/51-wireless.network

    # ---- wlan0 definition ----
    install -d ${D}/etc/wpa_supplicant
    install -m 0644 ${WORKDIR}/wpa_supplicant-wlan0.conf ${D}/etc/wpa_supplicant/wpa_supplicant-wlan0.conf

    # ---- SSH AUTHORIZED KEY ----
    install -d ${D}/home/root/.ssh
    install -m 0600 ${WORKDIR}/authorized_keys ${D}/home/root/.ssh/authorized_keys

}

# Enable the wifi link in systemd. This makes it autostart
# but not first time
pkg_postinst_ontarget:${PN}() {
    systemctl enable wpa_supplicant@wlan0.service
}

# List the files that this recipe adds
FILES:${PN} = "\
    /usr/lib/systemd/network/51-wireless.network \
    /etc/wpa_supplicant/wpa_supplicant-wlan0.conf \
    /home/root/.ssh/authorized_keys \
"
 
 
Finally we create meta-bdp/recipes-core/dropbear/dropbear_%.bbappend to change the ssh behavior from disallowing root login to disallowing password login by replacing the -w argument with -s -g. That way we can still log in as root with the public key. A .bbappend file attaches to the build of the original file. The _% means that we will attach to any version of this file. In other words we are adding steps to do after that file is parsed, and it looks like this:
 
do_install:append() {
   sed -i 's/-w/-s -g/g' ${D}/etc/default/dropbear
 
Now it is a good time to check the layer. It should have this structure:
 
├── conf
│   └── layer.conf
├── COPYING.MIT
├── README
├── recipes-connectivity
│   └── wifi-profiles
│       ├── files
│       │   ├── 51-wireless.network
│       │   ├── authorized_keys
│       │   └── wpa_supplicant-wlan0.conf
│       
── wifi-profiles.bb
└── recipes-core
    ├── dropbear
    │   
── dropbear_%.bbappend
    └── images
        ├── bdp-image-deploy.bb
        └── bdp-image-devel.bb
 
 
Then we do the following to build and write this to the SD card (save this in a script - when adding new recipes later we will rerun this):
 
cd $BUILDDIR

bitbake bdp-image-devel

cd tmp-glibc/deploy/images/stm32mp15-disco/

rm FlashLayout_sdcard_stm32mp157f-dk2-opteemin.raw

scripts/create_sdcard_from_flashlayout.sh flashlayout_bdp-image-devel/opteemin/FlashLayout_sdcard_stm32mp157f-dk2-opteemin.tsv 

sudo umount `lsblk --list | grep mmcblk0 | grep part | gawk '{ print $7 }' | tr '\n' ' '`

sudo dd if=FlashLayout_sdcard_stm32mp157f-dk2-opteemin.raw of=/dev/mmcblk0 bs=8M conv=fdatasync status=progress



 Now it is time to check the connectivity by using the terminal that is offered via UART (so running picocom -b 115200 /dev/ttyACM0) - so not the terminal on the host machine. After first boot the wifi service is enabled, but not started (due to a race condition in the setup process). So do a single reboot:
 
shutdown -r now
 
Then after coming up again: 
 
ifconfig
 
You should now see the wlan0 interface having an IP address. And you can
 
ping google.com
 
To check the connection to the internet. Now the card has internet access via wifi, and it also (thanks to ZeroConf) has a name on your network. Looking at the prompt in picocom, it says something like
 
root@stm32mp15-disco-xx-yy-zz:~# 
 
The part in bold is your cards hostname (which can also be seen in /etc/hostname) and it will respond to a DNS lookup with that name. So if you:
 
ping  stm32mp15-disco-xx-yy-zz
 
from your host PC with the right substitution for xx-yy-zz  you will get an answer from the board. Now - you can also go
 
ssh root@stm32mp15-disco-xx-yy-zz 

Note that if you have rebuilt the image, ssh may complain that the fingerprint of the host have changed. To fix this you do as the error message tells you :)
  
Now we are ready for software development

 
 
 
 

mandag den 10. november 2025

Working with Yocto: Building an OpenSTLinux image

Back to overview

Building and booting a base image 

In order to work with OpenSTLinux (or any Yocto tree) you need to run Linux. OpenSTLinux is tested on Ubuntu 24.04 which is also what I use for this. You also need an SD card reader and an empty micro-SD card.

 To prepare the machine for development you (one time) need to install a number of packages - this command nicely does it all:

  sudo apt-get install -y gawk wget git-core diffstat unzip texinfo \
    build-essential chrpath socat cpio python3 python3-pip python3-pexpect \
    xz-utils debianutils iputils-ping libsdl1.2-dev xterm \
    repo bmap-tools bsdmainutils gcc-multilib git-lfs libgmp-dev \
     libmpc-dev libssl-dev lz4 pylint python3-git
  
You also need to configure your GIT identity, replacing the example mail and names with real ones:
  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"
Then we create a working directory for our stm32mp work, and fetch the sources (or rather description) of openstlinux using the repo tool:

mkdir -p ~/stm32mp/openstlinux && cd ~/stm32mp/openstlinux
repo init -u https://github.com/STMicroelectronics/oe-manifest.git \
  -b refs/tags/openstlinux-6.6-yocto-scarthgap-mpu-v25.08.27
repo sync

Now it is time to figure out which machine to build for and which distro to build.  

To list the machines that you can target with a given Yocto tree, you can do

find layers/ -path "*/conf/machine/*.conf" -printf "%f\n" | sed 's/\.conf$//' 

One of the machines is the stm32mp15-disco which is the discovery board description. That is the one we will be buildling for.

 To list the distros supported in the tree, you can do

find layers -path "*/conf/distro/*.conf" -printf "%f\n" | sed 's/\.conf$//'

The openstlinux distribution in this tree is called openstlinux-weston. 

To get started we need to start a development environment. While being inside ~/stm32mp/openstlinux we do:

DISTRO=openstlinux-weston MACHINE=stm32mp15-disco \
  source layers/meta-st/scripts/envsetup.sh
This drops you into ~/stm32mp/openstlinux/build-openstlinuxweston-stm32mp15-disco, which is the build directory for this machine. Now in principle we can start building, but to speed up the build we should enable multicore builds. You should specify 1,5x the number of threads as you have cores to balance usability of the machine and build time. To enable multicore builds open conf/local.conf in your favorite editor, and write this in the end
# Multicore build
# Run N BitBake tasks in parallel (fetch/compile/package, etc.)
BB_NUMBER_THREADS = "12"

# Pass -jN to make/ninja inside recipes
PARALLEL_MAKE = "-j 12"

Before building (and after each rebuild) we need to disable apparmor (on Ubuntu 24.04 as per 3/11/2025) as it interferes with bitbake - the tool Yocto uses for builds:
echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
Then we are ready to build. Note that you should find something else to do while doing this, as it can take very long. On my machine this step takes around 2,5 hours:
bitbake st-image-weston

Now it is time to make the SD card. Insert a fresh card in your SD card reader and eject it. Then we go to the directory where the image files have been put:

cd ./tmp-glibc/deploy/images/stm32mp15-disco/
  
In there we find
scripts/create_sdcard_from_flashlayout.sh
that can be used to either create an image to write to a flash card. . To generate the flash image we go:
./scripts/create_sdcard_from_flashlayout.sh \
flashlayout_st-image-weston/opteemin/FlashLayout_sdcard_stm32mp157f-dk2-opteemin.tsv
This creates a .raw file with the full disk image. Now follow the directions on the screen. First unmount all drives:
sudo umount `lsblk --list | grep mmcblk0 | grep part | \
gawk '{ print $7 }' | tr '\n' ' '`
Then write the image
sudo dd if=FlashLayout_sdcard_stm32mp157f-dk2-opteemin.raw \
of=/dev/mmcblk0 bs=8M conv=fdatasync status=progress

Note that the sd card can be called something else on your machine - on mine it is /dev/mmcblk0. Be patient and wait until the prompt returns for the card to actually be written. It may seem like the prompt locks up but it is actually just flushing cache. 

Now insert the programmed sd card in the evaluation board. Before booting the drive, connect to the discovery board using picocom:

picocom -b 115200 /dev/ttyACM0

and then power up the board. The terminal will now show the bootloader and linux kernel booting. During boot the file systems are put in the right size, so it will take a bit of time to boot. After boot you should see the demo screen show up, and if you press enter in the terminal, you will see a root prompt. 

Congratulations, you have now built your first linux image and booted it successfully.

lørdag den 8. november 2025

Working with Yocto: Basic concepts

Getting into embedded linux can be a daunting task, and the learning curve for working with Yocto is steep. In the following posts I will take you through how to build a linux image, create and debug an application and include that application in the image. 

But first we need to get a few things straight: Many speak of Yocto as an embedded linux distribution. It is not. Yocto is a framework for buildling linux distributions, i.e. it provides a set of tools to build, manage and install the many parts that constitutes a linux distribution into a flash image that can be used on a given platform. Software packages and add-ons are structured as "layers" that form a dependency tree. This allows extensions to be added without having to modify already existing code.

A distribution can be tailored specifically to the needs of the product that is being built in terms of content, machine architecture etc. etc. For a specific distribution you build, Yocto also can generate an SDK that you must use to develop apps for the distribution. This is because the available tools and libraries is dependent on the features and machine architecture enabled for the distribution. 

This series will use the OpenSTLinux system developed by ST for their stm32mp* processor series. It uses yocto as the development framework, but adds a couple of proprietary layers to the standard tree to support their hardware. Yocto supports many development models, but here I will deal with the single developer, single machine model, and I will use the stm32mp157f-dk2 development kit as my target platform. 

A typical development flow for a product running embedded linux consists of the following: 

All of the steps above are treated in the series. As it progresses, links will be added to point to the pages that  describes these concepts.