Skip to content

Support your machine (Arm)

This page suggests a step-by-step guide of the things to do in order to have Welma booting on your Arm-based machine, when it is based on a SOM or processor that is not supported by Welma.

Prerequisites

Before starting using Welma, you need a working BSP for your specific board:

  • Bootloader
  • Linux kernel

If you already have Yocto layers (for example provided by a SOM vendor), that build a bootable image, it is a good starting point.

Boot your Linux image and learn about your hardware. In particular:

  • What the boot sequence is (BL2, BL31, BL33,...)
  • From which storage the machine boots (eg: sd card, emmc boot area partition 1, emmc user area, SPI NOR flash, ...)
  • How to flash the bootloader
  • How to flash the Linux image

Step 1: Set up your Yocto BSP layer

  • Decide on a name for MACHINE
  • Create a layer named meta-<bsp-name> with:

    • conf/layer.conf
    • conf/machine/$MACHINE.conf
    • conf/templates/$MACHINE/local.conf.sample
    • conf/templates/$MACHINE/bblayers.conf.sample
  • Decide on the Yocto layers to use:

    • We generally recommend to remove dependencies on unnecessary layers that might be mentioned in the vendor's BSP layers (typically meta-freescale-*, meta-arm, meta-imx, ...). The reason is to make maintenance and upgrades easier. You generally do not need these extra layers. But if, for your project, you need a specific recipe of one of these layers, you can decide later either to use the whole layer or to write your own recipe directly in meta-<bsp-name>.

    • Integrate meta-welma

    • Create your manifest that references these layers and their exact references (tag or commit identifier).

    • Integrate these in bblayers.conf.sample, and so that all developers share the same reference.

  • Stick to Welma's default partition layout for now: boot, sysro, appro, sysrw.

  • Create your images recipes:

    • Production image with inherit welma-image
    • Development image with inherit welma-image-devel
  • Create your partition mapping in split/welma.part. See Partitioning. Eg:

part    boot    size=50M,update=ab,dev=/dev/mmcblk0p3:/dev/mmcblk0p4
part    sysro   size=400M,update=ab,dev=/dev/mmcblk0p5:/dev/mmcblk0p6
part    appro   size=15M,version=0.0,update=ab,dev=/dev/mmcblk0p7:/dev/mmcblk0p8
part    sysrw   size=400M,dev=/dev/mmcblk0p9
  • If using wic, use a custom WKS file that configures the partitions for the default Welma partition layout (boot, sysro, appro, sysrw). Make sure the indices of the partitions match those of split/welma.part.

  • At this point you should be able to build and flash your images and run your bootloader. It will probably not boot further, as the vendor's bootloader does not know how to boot the Welma image.

Step 2: Have Welma boot to initramfs

In this step, you have to decide from which storage the Linux kernel should be loaded and modify the U-Boot boot sequence accordingly.

The Linux kernel of the Welma image is a FIT image named fitImage.

In the examples below, the kernel is loaded from emmc 0 / user area / partition 1

U-Boot:

  • Create the default environment file distro-bootcmd.env:
Example of minimal default environment for machine sm2s-imx8plus-mbep5
/* Machine-specific parameters */
bootmedia=mmc
bootdev=0  /* mmc device number */
fit_loadaddr=0x48000000
console=ttymxc1,115200

evaluate_bootflags=
    echo Placeholder for evaluate_bootflags;
    bootpart=1 /* partition where 'fitImage' is located */

set_swk1_in_bootargs=echo Placeholder for set_swk1_in_bootargs;

boot_welma=
    run evaluate_bootflags;
    load ${bootmedia} ${bootdev}:${bootpart} ${fit_loadaddr} fitImage;
    setenv bootargs "${bootargs} console=${console}";
    run set_swk1_in_bootargs;
    bootm ${fit_loadaddr};
    echo "Boot FAILED. Resetting...";
    reset

distro_bootcmd=run boot_welma;
  • Have this default environment file integrated in u-boot binary (CONFIG_ENV_SOURCE_FILE) and remove #define CFG_EXTRA_ENV_SETTINGS that may be defined in your board file.

Linux kernel:

  • In conf/machine/$MACHINE.conf, define parameters needed for buiding the Linux kernel and its FIT image:

    • KERNEL_DEVICETREE: include dtb and overlays
    • Find available memory regions in RAM and set load addresses for kernel, fdt, fdt overlays, ramdisk. These addresses will be used in the memory context of U-Boot. Eg:
      UBOOT_DTB_LOADADDRESS  = "0x50000000"
      UBOOT_DTBO_LOADADDRESS = "0x51000000"
      UBOOT_LOADADDRESS      = "0x40480000"
      UBOOT_RD_LOADADDRESS   = "0x43800000"
      
  • Kernel recipe: inherit kernel-uboot-deploy

Execute:

  • Build and install the bootloader and all partitions on your board.

  • Start your board.

You should now see your board boot until a kernel panic error, which will be fixed in the next step.

...
U-Boot ...
...
Hit any key to stop autoboot: ...
...
## Loading kernel from FIT Image at ...
...
## Loading ramdisk from FIT Image at ...
...
## Loading fdt from FIT Image at ...
...
Starting kernel ...
[    0.000000] Booting Linux on physical CPU 0x0000000000 ...
...
[    2.878750] Run /init as init process
[    2.915409] initramfs: Starting
[    3.022323] initramfs: ERROR: no active partition for (sysro / ...). Expect undefined behaviour.
[    3.005157] initramfs: ERROR: no active partition for (boot /boot ...). Expect undefined behaviour.
[    3.044028] initramfs: ERROR: no active partition for (appro /app ...). Expect undefined behaviour.
[    3.183040] Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000100

Step 3: Support A/B partitions

Patition layout:

  • Allocate an extra partition for the bootflags.

    • Update split/welma.part accordingly if need be.
    • In conf/machine/$MACHINE.conf, define the BOOTFLAGS_ parameters, that indicate how userspace programs will locate the bootflags. Eg:
      BOOTFLAGS_PATH ?= "/dev/mmcblk0p2"
      BOOTFLAGS_OFFSET ?= "0x0"
      BOOTFLAGS_OFFSET_COPY ?= "0x200"
      
  • If using wic:

    • Add this bootflags partition in the WKS file.
    • Make sure that you have A/B partitions for boot, sysro, appro (2 adjacent copies of each)

U-Boot:

  • Add the command bootflags (in cmd/bootflags.c) and have it configured.

  • distro-bootcmd.env: add bootflags logic:

    • Recover if a previous bootflags writing was interrupted
    • Roll back if this is the second attempt to boot in test mode
    • Select the kernel FIT image partition to boot on
evaluate_bootflags=
    bootflags check; /* Recover from a previous interrupted writing */
    bootflags read;  /* loads bootflags_test_mode
                      *       bootflags_test_count
                      *       bootflags_first_active_partition
                      */

    if test $? != 0; then
        /* bootflags not present or corrupted */
        echo "Initialize bootflags recovery environment";
        bootflags set-recovery-env;
    fi;

    if test $bootflags_test_mode = 1; then
        echo "Test mode";
        if test $bootflags_test_count -ge 1; then
            /* cancel test and back to normal mode */
            echo "bootflags_test_count=$bootflags_test_count. Revert to normal mode.";
            bootflags abort-test;
            /* has also updated the values in memory (as bootflags read does) */
        fi;
    else
        echo "Normal mode";
    fi;

    bootpart=${bootflags_first_active_partition}; /* Select boot partition */

    if test $bootflags_test_mode = 1; then
        bootflags increment-test-count;
    fi;

Step 4: Minimal working functionalities

At this point you have to make sure:

  • The Linux commands reboot and reboot -f make your board reboot.
  • Your board has one ethernet interface configured with a static IP address. Example:
    # ip address
    ...
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
        link/ether 10:e7:7a:e1:93:9a brd ff:ff:ff:ff:ff:ff
        inet 169.254.0.1/16 brd 169.254.255.255 scope link eth0
           valid_lft forever preferred_lft forever
    [...]
        inet6 fe80::1/10 scope link
           valid_lft forever preferred_lft forever
    [...]
    
  • Your development image can be reached by SSH. Eg:

    $ ssh root@169.254.0.1
    Last login: Fri Jul 19 06:49:53 2024
    stm32mp15-disco-welma:~#
    
    For this, you may need to configure the network (routes) of your PC.

  • U-Boot can download an image via TFTP and install it on the mass memory of the board. This is needed for Welma's automatic testing. Eg:

    STM32MP> setenv ipaddr 192.168.1.20
    STM32MP> setenv serverip 192.168.1.12
    
    STM32MP> tftp welma/demo-image-headless-dev-stm32mp15-disco-welma.wic.gz
    Using ethernet@5800a000 device
    TFTP from server 192.168.1.12; our IP address is 192.168.1.20
    Filename 'welma/demo-image-headless-dev-stm32mp15-disco-welma.wic.gz'.
    Load address: 0xc2000000
    Loading: #################################################################
             #################################################################
             ########
             4.6 MiB/s
    done
    Bytes transferred = 2024248 (1ee338 hex)
    
    STM32MP> mmcid=0
    STM32MP> gzwrite mmc $mmcid $loadaddr $filesize
    

  • The software update mechanism is working:

    • Upload welma-test/files/swu/swu-without-verity/appro1.swu to your target (eg: to /tmp)
    • On your target:
      • execute: updatectl install /tmp/appro1.swu
      • reboot
      • check that the file /app/README-1.txt is there
      • abort and roll back: updatectl abort
      • check that the device reboots and comes back to its original partitions (updatectl status)

Step 5: Watchdog

To ensure correct rollback in case of the update of a flawed kernel, you need to use a hardware watchdog.

If your board has more than one HW watchdog device, make sure to use the same watchdog device in U-Boot and Linux kernel & userspace.

U-Boot:

  • Start the watchdog timer before starting the kernel, with a timeout of 30 to 60 s (to give the kernel time to start)

It is also useful to have the wdt command (CMD_WDT=y), for testing (see https://docs.u-boot.org/en/latest/usage/cmd/wdt.html).

Linux kernel:

  • Activate the driver to support the watchdog device
  • Have the kernel ping the watchdog device until a userspace program takes over (for at most 4 min) and do not stop the watchdog timer when this userspace program closes /dev/watchdog:
WATCHDOG_HANDLE_BOOT_ENABLED=y
WATCHDOG_NOWAYOUT=y
WATCHDOG_OPEN_TIMEOUT=240

systemd:

  • Configure systemd to service the watchdog device (/dev/watchdog0).

Step 6: Next steps

  • Populate the license digest in recipes-welma/images/license-digest.bbappend:

    • WELMA_LIC_DIGEST_BOOT: add the recipes of the packages of the bootloader
  • Customize your partitions

  • Configure secure storage. This support should be provided the bootloader which must integrate and launch OP-TEE OS. In Linux, use xtest (from package optee-test) to check correct support.

  • Configure data provisioning

  • Add your project-specific packages in your image

Step 7: Support secure boot

  • In conf/templates/$MACHINE/local.conf.sample, set WELMA_SECURE_BOOT = "1"

Bootloader:

  • Activate (or write) the code for authenticating each stage until U-Boot
  • Have it signed in the Yocto build with development keys
  • Create the tools for signing out of Yocto
  • Document how to provision the needed public keys within manufactured products

U-Boot:

We only consider here the standard U-Boot mechanism for authenticating the kernel FIT image with RSA public keys. Some vendor's versions of U-Boot use other mechanisms (such as HAB on imx-based boards), and this is not covered in this document.

  • Provision SWK1 in U-Boot

    • do_compile:append(): have SWK1 injected into the U-Boot binary using the script inject-pubkey-uboot-dtb
  • Have U-Boot pass SWK1 to the kernel (needed when SWK2 is not used):

    • Add the command fdt get hexvalue (in cmd/fdt.c)
    • distro-bootcmd.env: add SWK1 public key to bootargs
set_swk1_in_bootargs=
    fdt addr ${fdtcontroladdr};
    fdt get hexvalue rsa_exponent /signature/key-SWK1 rsa,exponent;
    fdt get hexvalue rsa_modulus /signature/key-SWK1 rsa,modulus;
    setenv bootargs "${bootargs} swk1=rsa_exponent:0x${rsa_exponent};rsa_modulus:0x${rsa_modulus}"

Linux kernel:

  • Enable dm-verity: CONFIG_DM_VERITY=y

Step 8: Production bootloader

Production image should not have an interactive bootloader.

Make sure to apply a minimal u-boot configuration when IMAGE_FEATURES contains development.

This minimal configuration should at least include CONFIG_BOOTDELAY=-2 to boot without offering any command line.