Use case 3: other HW¶
What typically differs from a BSP to another are:
- versions and source code repositories of bootloader and kernel
- device trees
- defconfig (selection of drivers...)
This page gives guidelines for supporting Welma on a custom hardware board.
Step 1: Learn about your HW platform¶
First you need to know:
- Which HW components and peripherals are on your board
- What storage you may use for your product
- How to do the cabling
Then you need complete build & boot procedures with the vendor's BSP (without Welma). This ensures that you have a working bootloader and Linux kernel with compatible drivers, device trees and defconfig.
If you were already provided with Yocto layers (for example provided by a SOM vendor), that build a bootable image, it is a good starting point. And you have to understand how the BSP Yocto layers of the vendor are organized.
Finally you need to know:
-
What the boot sequence of the board is (BL2, BL31, BL33,...)
-
How to boot your bootloader without flashing it (uuu, dfu-util, ...)
-
how to flash the Linux image
Step 2: Set up your Yocto BSP layer¶
-
Create a layer for your BSP (eg:
meta-<bsp-name>):- Decide on a name for MACHINE
- Create
conf/layer.conf - Create
conf/machine/$MACHINE.conf - Create
conf/templates/$MACHINE/{local,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 the revisions of the vendor's BSP (u-boot, optee-os, tf-a, kernel):
- SRC_URI, SRCREV, UBOOT_CONFIG, KERNEL_DEVICETREE, ...
-
Stick to Welma's default partition layout for now: boot, sysro, appro, sysrw.
-
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,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 (
bitbake welma-image-minimal-dev) and flash your image 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 3: Have Welma boot to initramfs¶
In this step, you have to explain to U-Boot from which storage the Linux kernel should be loaded ie. 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 4: Support A/B partitions¶
Partition 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 5: 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 can be helpful for automatic testing of new images. 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 6: 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 7: Finalization of basic support¶
-
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 8: 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 9: 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.