UBI volumes in Yocto

Posted by Marcus Folkesson on Saturday, December 14, 2024

UBI volumes in Yocto

These days, managed NAND flash memories such as eMMC or SD-cards has become more common in the embedded Linux systems as they abstract (or hide..) the complicated raw NAND memory under the hood. Even if raw NAND memories are more complicated to handle, they are still used in many new products out there.

When working with raw NAND flashes, UBI [1] with UBIFS on top of that is the way to go. It has - more or less, replaced all other flash file systems such as JFFS2 and YAFFS.

/media/nand-flashes.png

There is much to say about UBI/UBIFS, but this post is not about all the benefits with UBI/UBIFS but how to integrate it with Yocto.

The reason for this post is that the whole UBI/UBIFS part is pretty poorly described in the official documentation.

Supported IMAGE_TYPES

Yocto supports several image related to UBI/UBIFS with the default image_types [2] class, these are:

  • multiubi: Generate multiple UBI images with different parameters.
  • ubi: Generate a UBI image with one volume containing the root filesystem.
  • ubifs: Generate a UBIFS image containing the root file system.

First, add the type you want to generate to the IMAGE_TYPES variable [3], preferable in your machine configuration or local.conf. E.g:

1IMAGE_FSTYPES = "ubi ubifs"

You also have to provide parameters, MKUBIFS_ARGS and UBINIZE_ARGS, that should be passed to mkfs.ubifs and ubinize respectively. The parameters depends on your flash geometry. For example:

1MKUBIFS_ARGS = "-F -m 2048 -e 126976 -c 1968"
2UBINIZE_ARGS = "-m 2048 -p 128KiB -O 2048"

What if...

... I want to generate several UBI images with different parameters?

Consider that you have multiple revisions of a board where the NAND flash geometry differ and you want to build the very same image for each of them. Creating separate machines is kind of overkill as everything is the same but the arguments to mkfs.ubifs and ubinize.

This is where the multiubi comes in. It let you specify several UBI configurations. It is best described with an example:

 1IMAGE_FSTYPES = "tar.bz2 multiubi"
 2
 3# Build the following sizes
 4MULTIUBI_BUILD ?= "normal large"
 5
 6# 256MB ('normal' flash layout)
 7export MKUBIFS_ARGS_normal = "-F -m 2048 -e 124KiB -c 1912 -x zlib"
 8export UBINIZE_ARGS_normal = "-m 2048 -p 128KiB -s 2048"
 9
10# 2GB ('large' flash layout)
11export MKUBIFS_ARGS_large = "-F -m 4096 -e 248KiB -c 8124 -x zlib"
12export UBINIZE_ARGS_large = "-m 4096 -p 256KiB -s 4096"

... I want to have several volumes?

By default, the ubi image type will genere one UBI image with one volume containing the root filesystem. This is not always desirable. Most system will have several volumes containing, e.g.

  • Overlay volume
  • Read-only volume
  • Persistent volume
  • A/B volumes for the root filesystem
  • ...

The default configuration that is passed to ubinize is generated by the write_ubi_config() function in the image_types.bbclass:

 1write_ubi_config() {
 2	local vname="$1"
 3
 4	cat <<EOF > ubinize${vname}-${IMAGE_NAME}.cfg
 5[ubifs]
 6mode=ubi
 7image=${IMGDEPLOYDIR}/${IMAGE_NAME}${vname}.${UBI_IMGTYPE}
 8vol_id=0
 9vol_type=${UBI_VOLTYPE}
10vol_name=${UBI_VOLNAME}
11vol_flags=autoresize
12EOF
13}

We need to create our own class in order to override that function.

Create an empty bbclass file for now:

1touch ./meta-mfoc/classes/mfoc-image.bbclass

And add it to the list for image classes to use:

1IMAGE_CLASSES += "contrinex-image"

Two copies of the root filesystem

With a symmetric configuration for root filesystems where the bootloader alternate between the A and B area is pretty common.

To achive that we could setup something like this:

 1write_ubi_config() {
 2	local vname="$1"
 3
 4	cat <<EOF > ubinize${vname}-${IMAGE_NAME}.cfg
 5[rootA]
 6mode=ubi
 7image=${IMGDEPLOYDIR}/${IMAGE_NAME}.${UBI_IMGTYPE}
 8vol_id=0
 9vol_type=${UBI_VOLTYPE}
10vol_name=rootA
11
12[rootB]
13mode=ubi
14image=${IMGDEPLOYDIR}/${IMAGE_NAME}.${UBI_IMGTYPE}
15vol_id=1
16vol_type=${UBI_VOLTYPE}
17vol_name=rootB
18
19EOF
20}

Root filesystem with a bunch of empty volumes

It is possible to exclude the image= parameter and use instead vol_size= to set a fixed size for an empty volume.

For exmple:

 1write_ubi_config() {
 2	local vname="$1"
 3
 4	cat <<EOF > ubinize${vname}-${IMAGE_NAME}.cfg
 5[rootfs]
 6mode=ubi
 7image=${IMGDEPLOYDIR}/${IMAGE_NAME}${vname}.${UBI_IMGTYPE}
 8vol_id=0
 9vol_type=${UBI_VOLTYPE}
10vol_name=rootfs
11vol_size=50MiB
12
13[data]
14mode=ubi
15vol_id=1
16vol_type=${UBI_VOLTYPE}
17vol_name=data
18vol_size=10MiB
19
20[rodata]
21mode=ubi
22vol_id=2
23vol_type=${UBI_VOLTYPE}
24vol_name=rodata
25vol_size=10MiB
26
27[persistent]
28mode=ubi
29vol_id=3
30vol_type=${UBI_VOLTYPE}
31vol_name=persistent
32vol_size=10MiB
33
34EOF
35}

Summary

Yocto has all possibilites to create a customized UBI image after specific needs, but it is pretty undocumented and it is hard to find good examples.