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.confconf/machine/$MACHINE.confconf/templates/$MACHINE/local.conf.sampleconf/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
- Production image with
-
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:
/* 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_SETTINGSthat 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:
-
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.partaccordingly if need be. - In
conf/machine/$MACHINE.conf, define theBOOTFLAGS_parameters, that indicate how userspace programs will locate the bootflags. Eg:
- Update
-
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(incmd/bootflags.c) and have it configured.- Reference implementation: https://gitlab.com/witekio/rnd/theembeddedkit/welma/welma-bootflags/-/tree/master/u-boot
- Example of configuration: These must be consistent with the partition layout.
-
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
rebootandreboot -fmake 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:
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.swuto your target (eg: to/tmp) - On your target:
- execute:
updatectl install /tmp/appro1.swu - reboot
- check that the file
/app/README-1.txtis there - abort and roll back:
updatectl abort - check that the device reboots and comes back to its original
partitions (
updatectl status)
- execute:
- Upload
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:
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
-
Configure secure storage. This support should be provided the bootloader which must integrate and launch OP-TEE OS. In Linux, use
xtest(from packageoptee-test) to check correct support. -
Add your project-specific packages in your image
Step 7: Support secure boot¶
- In
conf/templates/$MACHINE/local.conf.sample, setWELMA_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 scriptinject-pubkey-uboot-dtb
-
Have U-Boot pass SWK1 to the kernel (needed when SWK2 is not used):
- Add the command
fdt get hexvalue(incmd/fdt.c) distro-bootcmd.env: add SWK1 public key tobootargs
- Add the command
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.