Version 61 (modified by 6 weeks ago) ( diff ) | ,
---|
-
Buildroot
- Suggested Tools
- Building
- initrd (initial ramdisk)
- initramfs
- Using Buildroot toolchain externally
- Busybox config
- Adding or modifying files in the rootfs
- Create a custom init script
- Loading kernel modules
- Kernel Configuration
- Venice (IMX8M)
- Newport (CN80XX)
- Ventana (IMX6)
- SWUpdate
- Other Buildroot Wiki Pages
Buildroot
A Linux kernel without a root filesystem (aka rootfs) is useless. There are many sources for root filesystems including complete Linux distributions like Ubuntu (often too big, or limited in arch availability), pre-built root filesystems from vendors (often too limited), root filesystems built manually with Busybox (still often too limited) and more. There are Embedded Linux build systems which try to be more flexible like OpenEmbedded, Yocto, and OpenWrt but these tend to be not easy to understand or quick to setup. Buildroot tends to be a much more simplistic approach using standard makefiles, can produce a root filesystem in minutes, and has 1000+ userspace libs/apps available.
Using a buildroot rootfs is extremely useful for:
- Small fast booting self-contained systems (the default busybox rootfs is typically ~1.5MB)
- Kernel development (using initrd or initramfs options)
- Using its toolchain externally
!*!*!*!* WARNING !*!*!*!*
Buildroot is a powerful and low footprint rootfs. However, it is for advanced developers due to the fact there is a lot of manual configuration. Gateworks recommends developing with a more friendly ready to go rootfs, such as Ubuntu where package management is provided by default. Development time will be shortened greatly with Ubuntu. Once operating properly on Ubuntu, one can move over to buildroot to optimize.
Note that Buildroot requires all packages to be installed at the time of building. There is not a package manager like many other distributions have (like Ubuntu, such as apt-get to quickly install pre-compiled package binaries.
Suggested Tools
Suggested tools to include for kernel development:
- dropbear for SSH
- benchmarksiozone, bonnie++, LTP, netperf, ramspeed, stress, lmbench, iostat, memtester, etc
- debug tools; evtest, i2c-tools, devmem2, pciutils, usbutils, libv4l, alsa-utils, linux-firmware, mii-diag, iperf, iw
- filesystem tools: resize2fs (BR2_PACKAGE_E2FSPROGS_RESIZE2FS) adds 1.2MB for 2.4MB cpio
Building
Install dependencies:
apt install build-essential libncurses-dev -y
Building:
git clone https://github.com/buildroot/buildroot.git cd buildroot make menuconfig # configure make -j8 ls output/images
- Note that like many build systems sources will be downloaded from the network during the build process
- The .config file contains all the configuration options from the
make menuconfig
- see sections below on configuration tips for various platforms
For platform specific notes see below:
References:
- https://buildroot.org
- https://buildroot.org/downloads/manual/manual.html
- http://free-electrons.com/pub/conferences/2013/kernel-recipes/rootfs-kernel-developer/rootfs-kernel-developer.pdf
initrd (initial ramdisk)
The initial RAM disk (initrd) is an initial root file system that is mounted prior to when the real root file system is available. The initrd is bound to the kernel and loaded as part of the kernel boot procedure.
An initrd
is a 'cpio' image (an archive created with the Unix cpio
tool) thus you need to have
BR2_TARGET_ROOTFS_CPIO enabled and optionally one of the compression formats supported by your kernel).
If using U-Boot 'bootm' be sure to enable BR2_TARGET_ROOTFS_CPIO_UIMAGE which runs 'mkimage' on output/images/rootfs.cpio to create a 'uramdisk'.
Example booting Linux kernel with device-tree and initrd using U-Boot 'bootm':
- Bootloader:
setexpr fdt_addr $loadaddr setexpr linux_addr $fdt_addr + 0x20000 # allow 128KB for FDT setexpr rd_addr $linux_addr + 0x4000000 # allow 64MB for kernel setenv bootargs "console=${console},${baudrate}" setenv fsload tftpboot # for network load $fsload $fdt_addr $fdt_file2 && $fsload $linux_addr uImage && $fsload $rd_addr uramdisk && bootm $linux_addr $rd_addr $fdt_addr
- In the above example you can modify fsload to load for your storage interface, device, and filesystem (ie "setenv fsload 'fatload mmc 0:1'" for loading from fist mmc controller first partition fatfs filesystem)
For Venice
In your buildroot config enable:
BR2_TARGET_ROOTFS_CPIO BR2_TARGET_ROOTFS_CPIO_GZIP BR2_TARGET_ROOTFS_CPIO_UIMAGE
Make and copy the necessary files to your hosted directory:
TFTPBOOT=<your hosted TFTP dir> cp buildroot/output/build/linux-v5.4.45-venice/arch/arm64/boot/dts/freescale/imx8mm-venice*.dtb $TFTPBOOT cp buildroot/output/images/Image buildroot/output/images/rootfs.cpio.uboot $TFTPBOOT
Boot your Venice, break out in the bootlaoder:
setenv serverip <your server IP address> setenv ipaddr <the IP for your board> setenv bootargs "console=${console},${baudrate}" setenv fsload tftpboot setenv fdt_file1 imx8mm-venice-gw72xx-0x.dtb #or whichever dtb your board requires $fsload $fdt_addr_r $fdt_file1 && $fsload $kernel_addr_r Image && $fsload $ramdisk_addr_r rootfs.cpio.uboot && booti $kernel_addr_r $ramdisk_addr_r $fdt_addr_r
initramfs
The buildroot root filesystem can also be built statically into a kernel eliminating the need to have a separate kernel and ramdisk as in the initrd option above.
To build a rootfs suitable for use as an initramfs:
- Select target arch
- Configure toolchain or point to external toolchain
- System configuration - select devtmpfs /dev management method and ensure serial port for the getty is correct
- Filesystem images - select cpio format
- use
make
to build - your rootfs will be in output/images/rootfs.cpio and will build within minutes - a default config using busybox will be about 1.5MiB
Make sure your kernel has the following:
- CONFIG_DEVTMPFS=y - to get devtmpfs support, to provide a dynamic /dev
- CONFIG_INITRAMFS_SOURCE="/path/to/buildroot/output/images/rootfs.cpio" - path to your cpio
- CONFIG_INITRAMFS_COMPRESSION_GZIP=y - compression algorithm
- CONFIG_INITRAMFS_ROOT_UID=0 - root user id
- CONFIG_INITRAMFS_ROOT_GID=0 - root group id
If using buildbot to build kernel add the following to automatically build a kernel using to buildroot rootfs as an initramfs:
- BR2_TARGET_ROOTFS_INITRAMFS=y
See also:
Using Buildroot toolchain externally
Buildroot builds its own GCC toolchain and using this externally can be useful. The toolchain generated by Buildroot is located by default in output/host/
and the simplest way to use it to to add output/host/bin/
to your PATH then and use the version of gcc tools there.
For example:
export PATH=$PWD/output/host/bin:$PATH export CROSS_COMPILE=arm-linux- export ARCH=arm
It is possible to relocate this toolchain making it easy for distribution using make sdk
which prepares the toolchain to be relocatable and creates a tarball in the output/host/
directory. The relocate-sdk.sh
script in the tarball can be used to update paths.
See the full documentation in docs/manual/using-buildroot-toolchain.txt
Busybox config
Busybox is used by default for all of the tools in the rootfs built by buildroot. If you want to alter the default configuration of busybox itself you can access it via make menuconfig
:
- Target packages -> BusyBox -> BusyBox configuration file to use
- defaults to package/busybox/busybox.config
Adding or modifying files in the rootfs
A common need is to add or modify existing files in the root filesystem. Buildroot makes this very easy by allowing a set of directories to be specified that it will overlay on the target root filesytem.
Examples
- Add firmware for the Sterling LWB wireless module (obtained from https://www.lairdconnect.com/wireless-modules/wi-fi-bt-modules/sterling-lwb)
- make menuconfig and set BR2_ROOTFS_OVERLAY=files
- create an overlay directory. For example, add firmware for the LAIRD sterling-lwb wifi module:
mkdir -p files tar xvf firmware-7.0.0.326.tar.bz2 -C files
- build with 'make'
- notice your files are now in output/target/lib/firmware/brcm
If you wish to modify a file simply provide your version of it in your overlay and it will be copied over the original. If you wish to remove a file you can provide a 0-byte version of the file which may meet your needs.
Additionally you can use BR2_ROOTFS_POST_BUILD_SCRIPT
to make any changes after buildroot builds but before the rootfs images are assembled.
Create a custom init script
The init
program is the first userspace program started by the kernel (with PID 1) and is responsible for starting the userspace services. There are three different types of init provided by buildroot which can be chosen under 'System cnfiguration, Init system' but the default solution provided by busybox is usually just fine for embedded systems.
The busybox init system is configured via an {{{/etc/inittab{{{ which has a fairly simple syntax (see here). The important items in this file do the following:
- mount various psuedo-filesystems such as proc
- execute /etc/init.d/rcS on startup
- execute /etc/init.d/rcK on shutdown
- kick off a getty process on the console tty allowing login
The /etc/init.d/rcS script executes the scripts in /etc/init.d which match the 'S??*' pattern. If you want a custom init script you can add one here. A good example to start with is the /etc/init.d/S40network example.
For more information see the Buildroot user manual 'init system' section.
Loading kernel modules
Typical Linux systems have a hotplug manager such as udev
running in the background that loads modules for detected devices. Without such as system you must do this manually. Once easy solution is to do this at boot time via an init script.
Examples:
- Auto load modules for devices detected at boot as well as a non-device specific module such as 'batman-adv':
- make menuconfig and set BR2_ROOTFS_OVERLAY=files
- create an init script that loads modules for detected devices and modules listed in
/etc/modules
:mkdir -p files/etc/init.d cat << \EOF > files/etc/init.d/S30modules #!/bin/sh case "$1" in start) printf "Loading modules:" # load modules for detected devices find /sys/ -name modalias -print0 | xargs -0 sort -u -z | xargs -0 modprobe -abq # load modules from /etc/modules [ -r /etc/modules ] || exit 0 while read module args; do case "$module" in ""|"#"*) continue ;; esac # attempt to load modules modprobe ${module} ${args} >/dev/null done < /etc/modules ;; esac EOF chmod +x files/etc/init.d/S30modules
- create an
/etc/modules
file with extra modules and arguments you wish to load:cat << \EOF > files/etc/modules batman-adv EOF
- build with 'make'
Kernel Configuration
If you are using buildroot to build a kernel BR2_KERNEL
, you can choose where to get the kernel config (the in-kernel arch default, another in-kernel defconfig, or a custom kernel defconfig). Regardless of your initial kernel config choice you can later modify the kernel configuration via:
make linux-menuconfig # invoke kernel menuconfig make linux-savedefconfig # invoke kernel savedefconfig make linux-update-defconfig # copy the kernel defconfig to the path specified by {{{BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE}}}
The kernel package name is 'linux' therefore you can use the following if needed:
make linux-build # execute kernel build make linux-rebuild # rebuild kernel make linux-clean # clean the kernel
Venice (IMX8M)
The following details pertain to buildroot 2024.02 although newer versions will likely be similar if not the same.
The Venice product family is based on the i.MX8MM, i.MX8MN, and i.MX8MP SoCs which have 4x Cortex-A53 CPU cores. Therefore the 'BR2_aarch64' is really the only important configuration to produces binaries executable on arm64 CPU's.
To build a root filesystem only:
cat << EOF > configs/arm64_minimal_rootfs_defconfig # arm64 arch BR2_aarch64=y # filesystem options BR2_TARGET_ROOTFS_CPIO=y BR2_TARGET_ROOTFS_CPIO_XZ=y BR2_TARGET_ROOTFS_EXT2=y BR2_TARGET_ROOTFS_EXT2_XZ=y BR2_TARGET_ROOTFS_TAR_XZ=y EOF make arm64_minimal_rootfs_defconfig make -j8
This builds a ~1.3MiB output/images/{root.tar,rootfs.cpio,rootfs.ext2}.xz consisting of a ~3.6MiB root filesystem (when uncompressed) in a couple of minutes of building on a typical Linux desktop.
If you also want buildroot to build a kernel with a minimal kernel config as well as the minimal root filesystem you could use the following:
cat << EOF > configs/venice_kernel_defconfig # arm64 arch BR2_aarch64=y # kernel BR2_KERNEL_HEADERS_6_6=y BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_CUSTOM_GIT=y BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://github.com/Gateworks/linux-venice.git" BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="v6.6.8-venice" BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="venice_minimal_kernel_6.6_defconfig" # filesystem options BR2_TARGET_ROOTFS_CPIO=y BR2_TARGET_ROOTFS_CPIO_XZ=y BR2_TARGET_ROOTFS_EXT2=y BR2_TARGET_ROOTFS_EXT2_XZ=y BR2_TARGET_ROOTFS_TAR_XZ=y EOF # fetch minimal kernel config for venice wget http://dev.gateworks.com/buildroot/venice/minimal/venice_minimal_kernel_6.6_defconfig # build make venice_kernel_defconfig make -j8 # copy venice DTB's to output dir (buildroot does not do this for you) cp output/build/linux-v6.6.8-venice/arch/arm64/boot/dts/freescale/imx8m*venice*dtb output/images/
This builds a ~1.3MiB output/images/{root.tar,rootfs.cpio,rootfs.ext2}.xz consisting of a ~3.6MiB root filesystem (when uncompressed) and a ~15MiB kernel Image and dtbs in a couple of minutes of building on a typical Linux desktop. This is a kernel Image, dtbs, and separate ramdisk based rootfs. This is a really easy way to get a read-only minimal Linux image that boots fast and does not access flash storage.
Perhaps an even more useful image would contain a basic tools for provisioning a FLASH emmc device:
cat << EOF > configs/venice_example_defconfig # arm64 arch BR2_aarch64=y # toolchain BR2_TOOLCHAIN_BUILDROOT_CXX=y # kernel BR2_KERNEL_HEADERS_6_6=y BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_CUSTOM_GIT=y BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://github.com/Gateworks/linux-venice.git" BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="v6.6.8-venice" BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="venice_minimal_kernel_6.6_defconfig" # extra utils needed for basic testing and provisioning emmc BR2_PACKAGE_BUSYBOX_SHOW_OTHERS=y BR2_PACKAGE_PV=y BR2_PACKAGE_STRESS=y BR2_PACKAGE_AUTOFS=y BR2_PACKAGE_E2FSPROGS=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_CRYPTSETUP=y BR2_PACKAGE_EVTEST=y BR2_PACKAGE_I2C_TOOLS=y BR2_PACKAGE_MEMTESTER=y BR2_PACKAGE_PARTED=y BR2_PACKAGE_PCIUTILS=y BR2_PACKAGE_PICOCOM=y BR2_PACKAGE_UBOOT_TOOLS=y BR2_PACKAGE_UBOOT_TOOLS_MKIMAGE=y BR2_PACKAGE_LIBUSB=y BR2_PACKAGE_TCPDUMP=y BR2_PACKAGE_WGET=y BR2_PACKAGE_SCREEN=y BR2_PACKAGE_COREUTILS=y BR2_PACKAGE_TAR=y BR2_PACKAGE_TPM2_ABRMD=y BR2_PACKAGE_TPM2_TOOLS=y BR2_PACKAGE_UTIL_LINUX_BINARIES=y # filesystem options BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y BR2_TARGET_ROOTFS_CPIO=y BR2_TARGET_ROOTFS_CPIO_XZ=y BR2_TARGET_ROOTFS_EXT2=y BR2_TARGET_ROOTFS_EXT2_XZ=y BR2_TARGET_ROOTFS_TAR_XZ=y EOF # fetch minimal kernel config wget http://dev.gateworks.com/buildroot/venice/minimal/venice_minimal_kernel_6.6_defconfig # build make venice_example_defconfig make -j8 # copy venice DTB's to output dir (buildroot does not do this for you) cp output/build/linux-v6.6.8-venice/arch/arm64/boot/dts/freescale/imx8m*venice*dtb output/images/
This builds a ~12MiB output/images/{root.tar,rootfs.cpio,rootfs.ext2}.xz consisting of a ~57MiB root filesystem (when uncompressed) and a ~15MiB kernel Image and dtbs in a couple of minutes of building on a typical Linux desktop. This is a kernel Image, dtbs, and separate ramdisk based rootfs.
You can easily add your own files and scripts to the rootfs image by setting BR2_ROOTFS_OVERLAY to a directory or directories where your files are.
Additionally you could enable BR2_TARGET_ROOTFS_INITRAMFS to link the rootfs into the kernel itself resulting in a larger kernel with no need to load a separate ramdisk.
If you need to add back or modify kernel features you can do so with:
make linux-menuconfig # make your changes make -j8 # save and update the defconfig once you are happy with it make linux-savedefconfig linux-update-defconfig # probably a good idea to remove the build kernel to force it to build again if you made changes rm -rf output/build/linux- # and rebuild make -j8 # copy venice DTB's to output dir (buildroot does not do this for you) cp output/build/linux-v6.6.8-venice/arch/arm64/boot/dts/freescale/imx8m*venice*dtb output/images/
You can boot a Kernel+dtb+ramdisk via U-Boot like this:
- load from network tftpserver
setenv fsload tftpboot run loadfdt && tftpboot $kernel_addr_r Image && tftpboot $ramdisk_addr_r rootfs.cpio.xz && booti $kernel_addr_r $ramdisk_addr_r:$filesize $fdt_addr_r
- Note that the ramdisk argument needs the ramdisk size so be sure to load that last as tftpboot/load will set filesize to the thing it loaded
- load from microSD with ext/fat filesystem
setenv fsload load mmc 1:1 run loadfdt && tftpboot $kernel_addr_r Image && booti $kernel_addr_r - $fdt_addr_r
- load from USB with ext/fat filesystem
usb start setenv fsload load usb 0:1 run loadfdt && tftpboot $kernel_addr_r Image && booti $kernel_addr_r - $fdt_addr_r
A prebuilt image can be found here which contains an image built from the above venice_example_defconfig
- Gateworks Venice Linux 6.6 kernel
- glibc with wide-char, locale, and c++ support
- screen (BR2_PACKAGE_SCREEN)
- pciutils (BR2_PACKAGE_PCIUTILS)
- libusb (BR2_PACKAGE_LIBUSB)
- eudev (BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV) (required for usbutils)
- usbutils (BR2_PACKAGE_USBUTILS)
- stress (BR2_PACKAGE_STRESS)
- evtest (BR2_PACKAGE_EVTEST)
- parted/gdisk/sgdisk disk partitioning tools
- ext filesystem support mkfs/resize2fs (BR2_PACKAGE_E2FSPROGS_RESIZE2FS)
- cryptsetup (BR2_PACKAGE_CRYPTSETUP) for using dm-crypt with LUKS for disk encryption
- u-boot image creation tools (mkimage/fw_setenv/fw_printenv)
Newport (CN80XX)
The following details pertain to buildroot 2020.08 although newer versions will likely be similar if not the same.
The Newport product family is based on the CN80xx SoC which has 4x Cortex-A53 like compatible CPU cores. Therefore the 'BR2_aarch64' is really the only important configuration which produces binaries executable on arm64 CPU's.
To build a root filesystem only:
cat << EOF > configs/arm64_minimal_defconfig # arm64 arch BR2_aarch64=y # filesystem options BR2_TARGET_ROOTFS_TAR_XZ=y EOF make arm64_minimal_defconfig make -j8
This builds output/images/root.tar.xz consisting of a ~1.7MiB root filesystem (when uncompressed) in 5 to 10 minutes of building on a typical Linux desktop.
If you also want buildroot to build a Gateworks v5.4.45 kernel with a minimal kernel config attached as an initrd you can use this:
cat << EOF > configs/newport_kernel_defconfig # arm64 arch BR2_aarch64=y # toolchain BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_5_4=y # kernel BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_CUSTOM_GIT=y BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://github.com/Gateworks/linux-newport.git" BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="v5.4.45-newport" BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="newport_minimal_kernel_defconfig" # filesystem options BR2_TARGET_ROOTFS_TAR_XZ=y BR2_TARGET_ROOTFS_INITRAMFS=y EOF # fetch minimal kernel config wget http://dev.gateworks.com/buildroot/newport/minimal/newport_minimal_kernel_defconfig make newport_kernel_defconfig make -j8
This produces a ~9MiB output/images/Image in 10 to 15 minutes of building on a typical Linux desktop. This is a kernel Image containing the root filesystem in a ramdisk. This is a really easy way to get a read-only minimal Linux image that boots fast and does not access flash storage.
Perhaps an even more useful image would contain tools for provisioning a FLASH emmc device:
cat << EOF > configs/newport_provision_defconfig # arm64 arch BR2_aarch64=y # toolchain BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_5_4=y BR2_TOOLCHAIN_BUILDROOT_WCHAR=y BR2_TOOLCHAIN_BUILDROOT_CXX=y BR2_TOOLCHAIN_BUILDROOT_LOCALE=y BR2_TOOLCHAIN_BUILDROOT_GLIBC=y # kernel BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_CUSTOM_GIT=y BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://github.com/Gateworks/linux-newport.git" BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="v5.4.45-newport" BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="newport_minimal_kernel_defconfig" # filesystem options BR2_TARGET_ROOTFS_CPIO_XZ=y BR2_TARGET_ROOTFS_INITRAMFS=y BR2_TARGET_ROOTFS_TAR_XZ=y # extra utils needed for basic testing and provisioning emmc BR2_PACKAGE_BUSYBOX_SHOW_OTHERS=y BR2_PACKAGE_COREUTILS=y BR2_PACKAGE_PV=y BR2_PACKAGE_STRESS=y BR2_PACKAGE_E2FSPROGS=y BR2_PACKAGE_E2FSPROGS_RESIZE2FS=y BR2_PACKAGE_PARTED=y BR2_PACKAGE_EVTEST=y BR2_PACKAGE_GPTFDISK=y BR2_PACKAGE_GPTFDISK_SGDISK=y BR2_PACKAGE_I2C_TOOLS=y BR2_PACKAGE_MEMTESTER=y BR2_PACKAGE_PCIUTILS=y BR2_PACKAGE_PICOCOM=y BR2_PACKAGE_LIBUSB=y BR2_PACKAGE_TCPDUMP=y BR2_PACKAGE_SCREEN=y BR2_PACKAGE_UTIL_LINUX=y BR2_PACKAGE_UTIL_LINUX_BINARIES=y BR2_PACKAGE_TAR=y # extra utils needed for uboot images and env BR2_PACKAGE_UBOOT_TOOLS=y BR2_PACKAGE_UBOOT_TOOLS_MKIMAGE=y EOF # fetch minimal kernel config wget http://dev.gateworks.com/buildroot/newport/minimal/newport_minimal_kernel_defconfig make newport_kernel_defconfig make -j8
- Note you can easily add your own files and scripts to this image by setting BR2_ROOTFS_OVERLAY to a directory or directories where your files are
This produces a ~15MiB output/images/Image in 10 to 15 minutes of building on a typical Linux desktop. This is a kernel Image containing the root filesystem in a ramdisk. This is a really easy way to get a read-only minimal Linux image that boots fast and has the tools necessary to create a partition table and filesystems
You can boot a Kernel+ramdisk via U-Boot like this:
- load from network tftpserver
tftpboot $kernel_addr_r Image && booti $kernel_addr_r - $fdtcontroladdr
- load from microSD with ext/fat filesystem
load mmc 1:1 $kernel_addr_r Image && booti $kernel_addr_r - $fdtcontroladdr
- load from USB with ext/fat filesystem
usb start; load usb 0:1 $kernel_addr_r Image && booti $kernel_addr_r - $fdtcontroladdr
A prebuilt image can be found here which contains an image built from the above newport_example_defconfig
- Gateworks Newport Linux 5.4.45 kernel
- glibc with wide-char, locale, and g++ support
- screen (BR2_PACKAGE_SCREEN)
- pciutils (BR2_PACKAGE_PCIUTILS)
- libusb (BR2_PACKAGE_LIBUSB)
- eudev (BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV) (required for usbutils)
- usbutils (BR2_PACKAGE_USBUTILS)
- stress (BR2_PACKAGE_STRESS)
- evtest (BR2_PACKAGE_EVTEST)
- parted/gdisk/sgdisk disk partitioning tools
- ext filesystem support mkfs/resize2fs (BR2_PACKAGE_E2FSPROGS_RESIZE2FS)
- u-boot image creation tools (mkimage/fw_setenv/fw_printenv)
Building .ext4 filesystem and compressed disk image
In the event you choose to use Buildroot but wish to avoid the drawbacks of a ramdisk you can install it to a block storage device. For this you will need to create a .ext4 filesystem image. This example will utilize the Linux-Newport branch of the mainline 4.14.4 kernel.
Prerequisite for this procedure you will need to have:
- Downloaded and configured the Newport BSP, many of the tools used will be provided by it.
- The linux-newport.tar.xz kernel tarball.
- The latest firmware-newport.img boot firmware if you would like to create a compressed disk image.
With the Newport BSP installed and Buildroot cloned set target architecture to AArch64:
cd buildroot cat << EOF > configs/my_defconfig BR2_aarch64=y EOF
Now:
make my_defconfig make -j8
After the build completes:
- Export the location of the rootfs.tar located in your buildroot/output/images folder.
ROOTFS=${PWD}/output/images/rootfs.tar
- Create a tmp_mnt directory at a location of your discression, this is your temporary mounting point for compiling the filesystem.
mkdir tmp_mnt TMP_MNT=${PWD}/tmp_mnt
- Create a filesystem of a specific size. It will be expandable later using resize2fs, make it large enough to fit what you have in your current build.
OUT=buildroot-newport.ext4 SIZEMB=1536 # 1.5GB truncate -s ${SIZEMB}M ${OUT} mkfs.ext4 -q -F -L rootfs ${OUT}
- Mount this file to your 'tmp_mnt' directory
sudo mount ${OUT} ${TMP_MNT}
- Extract the Buildroot rootfs.tar and linux-newport.tar.xz which was downloaded to this mount point.
sudo tar -C ${TMP_MNT} -xf ${ROOTFS} sudo tar -C ${TMP_MNT} -xf linux-newport.tar.xz
- Convert the kernel 'Image' (uncompressed Kernel) to a fit image. For this you will need a tool from your Newport BSP directory, in this example the path to the BSP directory is named ${NEWPORT_BSP}.
mv ${TMP_MNT}/boot/Image vmlinux gzip -f vmlinux mkimage -f auto -A arm64 -O linux -T kernel -C gzip -n "buildroot-newport" \ -a 20080000 -e 20080000 -d vmlinux.gz tmp_mnt/boot/kernel.itb
- Create U-Boot bootscript using the existing Ubuntu one from the Newport BSP, use mkimage to add the u-boot header.
mkimage -A arm64 -T script -C none -d ${NEWPORT_BSP}/newport/ubuntu.scr ${TMP_MNT}/boot/newport.scr
- Unmount temporary mount point, and compress the .ext4 file to be used with the u-boot command 'gzwrite'.
umount ${TMP_MNT} rm -rf ${TMP_MNT} gzip -k -f ${OUT}
To load this root file system without disturbing the existing boot firmware tftpboot can be used:
- Boot your Newport SBC, at the prompt "Hit any key to stop autoboot" press a key.
- Execute the following commands.
setenv ipaddr <your boards IP> setenv serverip <your TFTP server IP> setenv dev 0 setenv image buildroot-newport.ext4.gz run update_rootfs
To create a disk image from the .ext4 file:
- Creating a disk image is useful if you would like to overwrite the existing boot firmware when the image is flashed.
# http://dev.gateworks.com/newport/boot_firmware/firmware-newport.img cp firmware-newport.img buildroot-newport.img # copy buildroot rootfs .ext4 filesystem to image with an offset dd if=buildroot-newport.ext4 of=buildroot-newport.img bs=16M seek=1 # compress it gzip -k -f buildroot-newport.img
Ventana (IMX6)
The following details pertain to buildroot 2020.08 although newer versions will likely be similar if not the same.
The Ventana product family is based on the i.MX6 SoC which has ARM Cortex-A9 CPU cores. Therefore we will tune the compiler to build arm32 code with support for ARM NEON and VFP.
To build a root filesystem only:
cat << EOF > configs/imx6_minimal_defconfig # arm cortex-a9 cpu BR2_arm=y BR2_cortex_a9=y BR2_ARM_ENABLE_NEON=y BR2_ARM_ENABLE_VFP=y BR2_ARM_FPU_VFPV3=y # filesystem options BR2_TARGET_ROOTFS_CPIO_XZ=y EOF make imx6_minimal_defconfig make -j8
This builds output/images/root.tar.xz consisting of a ~1.7MiB root filesystem (when uncompressed) in 5 to 10 minutes of building on a typical Linux desktop.
If you also want buildroot to build a Gateworks v5.4.45 kernel with a minimal kernel config and a self-contained minimal root filesystem you would add the following the your defconfig:
cat << EOF > configs/ventana_kernel_defconfig # arm cortex-a9 cpu BR2_arm=y BR2_cortex_a9=y BR2_ARM_ENABLE_NEON=y BR2_ARM_ENABLE_VFP=y BR2_ARM_FPU_VFPV3=y # toolchain BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_5_4=y # kernel BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_CUSTOM_GIT=y BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://github.com/Gateworks/linux-imx6.git" BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="gateworks_5.4.45" BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="gwventana_minimal_kernel_defconfig" # filesystem options BR2_TARGET_ROOTFS_TAR_XZ=y BR2_TARGET_ROOTFS_INITRAMFS=y EOF # fetch minimal kernel config for ventana wget http://dev.gateworks.com/buildroot/ventana/minimal/gwventana_minimal_kernel_defconfig make ventana_kernel_defconfig make -j8
This produces a ~7MiB compressed kernel image in output/images/uImage containing the ~1.7MiB root filesystem in 15 to 20 minutes of building on a typical Linux desktop.
If you need add back or modify kernel features you can do so with:
make linux-menuconfig # make your changes make -j8 # save and update the defconfig once you are happy with it make linux-savedefconfig linux-update-defconfig
To boot a kernel+ramdisk on Ventana via the bootloader with a tftpserver copy output/images/uImage and output/images/*.dtb to your tftpserver and use:
- load from network tftpserver
setenv bootargs console=ttymxc1,115200 setenv fsload tftpboot setenv bootdir . # set this to the prefix of your tftp dir run loadfdt && $fsload $loadaddr $bootdir/uImage && bootm $loadaddr - $fdt_addr
- load from microSD with ext/fat filesystem
setenv bootargs console=ttymxc1,115200 setenv fsload load mmc 0:1 setenv bootdir . # set this to the prefix of your tftp dir run loadfdt && $fsload $loadaddr $bootdir/uImage && bootm $loadaddr - $fdt_addr
- load from USB with ext/fat filesystem
setenv bootargs console=ttymxc1,115200 setenv fsload load usb 0:1 usb start setenv bootdir . # set this to the prefix of your tftp dir run loadfdt && $fsload $loadaddr $bootdir/uImage && bootm $loadaddr - $fdt_addr
A prebuilt image can be found here which contains:
- Gateworks Linux 5.4.45 kernel (minimal kernel features)
- resize2fs (BR2_PACKAGE_E2FSPROGS_RESIZE2FS)
- uclibc
- screen (BR2_PACKAGE_SCREEN)
- pciutils (BR2_PACKAGE_PCIUTILS)
- libusb (BR2_PACKAGE_LIBUSB)
- eudev (BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV) (required for usbutils)
- usbutils (BR2_PACKAGE_USBUTILS)
- gdisk/sgdisk disk partitioning tools:
- gdisk/sgdisk (BR2_PACKAGE_GPTFDISK/BR2_PACKAGE_GPTFDISK_SGDISK)
- stress (BR2_PACKAGE_STRESS)
- evtest (BR2_PACKAGE_EVTEST)
- ubi/ubifs tools
SWUpdate
SWUpdate is a framework for providing firmware udpates. It is extremely flexible and provides support for many different scenarios. Because it exists as a package for buildroot it is a great choice for providing firmware updates when using a buildroot solution.
Here we will provide an example of building an SWUpdate Over-The-Air (OTA) update for buildroot with the following considerations:
- Symmetric Image Update (Two copies of rootfs which works well when your filesystem is relatively small compared to memory/flash space)
- We will not bother updating boot firmware (can be added later)
- We will not bother updating GSC firmware (can be added later)
- We will build the Gateworks 5.45 kernel for newport with a minimal kernel config
- We will use an uncompressed kernel image (just avoids needing to kernel a kernel.itb)
- We will use a modified uboot environment to handle our root partition toggling
For Newport
Here are the relevant files to add to your buildroot directory:
- configs/newport_swupdate_defconfig: Buildroot defconfig (represents the minimal 'changes' made to buildroot default config):
BR2_aarch64=y BR2_KERNEL_HEADERS_5_4=y # we will add some files to the rootfs from the 'overlay' subdir BR2_ROOTFS_OVERLAY="overlay" # # Kernel: # we will build the gateworks linux kernel using arm64 defconfig # plus some additional configs via newport_kernel_defconfig BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_CUSTOM_GIT=y BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://github.com/Gateworks/linux-newport.git" BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="v5.4.45-newport" BR2_LINUX_KERNEL_USE_ARCH_DEFAULT_CONFIG=y BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES="newport_kernel_defconfig" BR2_LINUX_KERNEL_INSTALL_TARGET=y # # Packages: # we need u-boot env tools for fw_setenv support in our scripts # we need zlib/openssl/libconfig/json for various features in SWUpdate we enable # we will use swupdate.config to configure SWUpdate BR2_PACKAGE_UBOOT_TOOLS=y BR2_PACKAGE_ZLIB=y BR2_PACKAGE_OPENSSL=y BR2_PACKAGE_LIBCONFIG=y BR2_PACKAGE_JSON_C=y BR2_PACKAGE_LIBCURL=y BR2_PACKAGE_SWUPDATE=y BR2_PACKAGE_SWUPDATE_CONFIG="swupdate.config" # # Filesystem # BR2_TARGET_ROOTFS_EXT2=y BR2_TARGET_ROOTFS_EXT2_4=y BR2_TARGET_ROOTFS_EXT2_GZIP=y
- Note that SWUpdate reuqires ZLIB if we are going to use gzip compression
- newport_kernel_defconfig: Kernel config fragment that adds OcteonTX and newport drivers to arm64 kernel defconfig
# Additionally OcteonTX peripheral drivers CONFIG_PCI_HOST_THUNDER_PEM=y CONFIG_PCI_HOST_THUNDER_ECAM=y CONFIG_CAN=y CONFIG_CAN_MCP251X=y CONFIG_THUNDER_NIC_PF=y CONFIG_THUNDER_NIC_VF=y CONFIG_MDIO_BITBANG=y CONFIG_I2C=y CONFIG_I2C_THUNDERX=y CONFIG_SPI_THUNDERX=y CONFIG_GPIO_THUNDERX=y CONFIG_MMC_CAVIUM_THUNDERX=y CONFIG_EDAC_THUNDERX=y CONFIG_EXT4_FS=y # Gateworks Newport GSC drivers CONFIG_MFD_GATEWORKS_GSC=y CONFIG_SENSORS_GSC=y
- overlay/etc/fw_env.config: config file for u-boot env tools fw_setenv
# Device offset size /dev/mmcblk0 0xff0000 0x8000 /dev/mmcblk0 0xff8000 0x8000
- Note that this file is whitespace sensitive
- swupdate.config: config file to build swupdate executable which resides on your firmware and drives the update process
# We do not need MTD or LUA support # CONFIG_MTD is not set # CONFIG_LUA is not set CONFIG_SIGNED_IMAGES=y CONFIG_ENCRYPTED_IMAGES=y # Suricatta provides support for fetching updates via a Hawkbit server if desired CONFIG_SURICATTA=y CONFIG_SURICATTA_SSL=y CONFIG_SURICATTA_STATE_CHOICE_BOOTLOADER=y # We need the raw handler to image to an MMC partition CONFIG_RAW=y # We need the shellscript handler for our update.sh shellscript CONFIG_SHELLSCRIPTHANDLER=y # We need the bootloader handler to alter the u-boot environment CONFIG_BOOTLOADERHANDLER=y
- sw-description: part of the actual firmware OTA which describes the process and file manifest of the update image. See here for syntax
software = { version = "0.1.0"; description = "Firmware update for XXXXX Project"; /* images installed to the system */ images: ( { filename = "rootfs.ext4.gz"; device = "/dev/update"; type = "raw"; compressed = true; } ); scripts: ( { filename = "update.sh"; type = "shellscript"; } ); }
- update.sh: This is the script that SWUpdate runs which we use as both a preinst and psotinst script (via cmdline). We determine the current root device and, flip it, and symlink /dev/update to the device to update to. We don't have to do the image install as we've configured SWUpdate to do that for us in sw-descrption images.
#!/bin/sh if [ $# -lt 1 ]; then exit 0; fi function get_current_root_device { for i in `cat /proc/cmdline`; do if [ ${i:0:5} = "root=" ]; then CURRENT_ROOT="${i:5}" fi done } # ping-pong between /dev/mmcblk0p2 and /dev/mmcblk0p3 # (adapt for your partitioning scheme and/or root device type) function get_update_part { CURRENT_PART="${CURRENT_ROOT:-/dev/mmcblk0p2}" if [ $CURRENT_PART = "/dev/mmcblk0p2" ]; then UPDATE_PART="3"; else UPDATE_PART="2"; fi } function get_update_device { UPDATE_ROOT=${CURRENT_ROOT%?}${UPDATE_PART} } if [ $1 == "preinst" ]; then # get the current root device get_current_root_device # get the device to be updated get_update_part get_update_device # create a symlink for the update process ln -sf $UPDATE_ROOT /dev/update fi if [ $1 == "postinst" ]; then # get the current root device get_current_root_device # get the device to be updated get_update_part get_update_device # toggle rootpart between 2 and 3 # we do it twice to write to both primary/secondary env fw_setenv mmcbootpart $UPDATE_PART fw_setenv mmcbootpart $UPDATE_PART fi
Once this is in place you can use the following to build:
# build buildroot rootfs make newport_swupdate_defconfig make # create a private/public key combo for signature verifcation openssl genrsa -out swupdate-priv.pem # private openssl rsa -in swupdate-priv.pem -out swupdate-public.pem -outform PEM -pubout # public # sign the sw-description openssl dgst -sha256 -sign swupdate-priv.pem sw-description > sw-description.sig # build swupdate image cp output/images/rootfs.ext4.gz . for i in sw-description sw-description.sig update.sh rootfs.ext4.gz; do echo $i; done | cpio -ov -H crc > my-software.swu
- Note that you can skip the inclusion of sw-description.sig and the pub/priv key if you don't care about signatures and have not define CONFIG_SIGNED_IMAGES in your swupdate.config
Here are some one-time steps you will need to do to your boot firmware:
- create an MBR partition table that defines LinuxA and LinuxB partitions we will ping-pong between. Note that the FATFS must not be changed from the original boot firmware generated MBR and that we also create a general userdata partition.
# 1: 2048:30720 (15MiB) FAT12 (required by boot firmware) # 2: 65536:4194304 (2GiB) LinuxA # 3: 4259840:4194304 (2GiB) LinuxB # 4: 8454144:4194304 (2GiB) userdata wget -q https://raw.githubusercontent.com/Gateworks/bsp-newport/sdk-10.1.1.0-newport/ptgen /bin/bash ptgen \ -p 0x01:2048:30720 \ -p 0x83:65536:4194304 \ -p 0x83:4259840:4194304 \ -p 0x83:8454144:4194304 \ > $BUILDROOT/output/images/mbr.bin
- In U-Boot we will update the partition table:
tftpboot ${loadaddr} mbr.bin && mmc dev 0 && mmc write ${loadaddr} 0 1 # mbr is at 0 and 1 sector long
- Note that if you ever update the entire boot firmware it will over-write this partition table so you will want to take care to not overwrite that portion
- In U-Boot we will install the original rootfs to the first Linux partition offset (LinuxA)
tftpboot ${loadaddr} rootfs.ext4.gz && gzwrite mmc 0 ${loadaddr} ${filesize} 0x100000 0x2000000 # rootfsA is at 0x2000000 (32MiB) and we use a 1MiB buffer
- In U-Boot we will alter the env to use the mmcbootpart env variable that our update.sh manipulates after a successful update:
setenv mmcbootpart 2 setenv bootcmd "setenv bootargs 'console=${console} root=/dev/mmcblk0p${mmcbootpart} rootwait rw usbcore.autosuspend=-1 kpti=0; load mmc 0:${mmcbootpart} ${kernel_addr_r} boot/Image' && booti ${kernel_addr_r} - ${fdtcontroladdr}" saveenv
- Note the single quotes around the bootargs value as we do not want U-Boot to expand the args until runtime
After you boot to buildroot you can fetch and install the SWUpdate image with:
# bring up networking udhcpc -i eth0 # fetch image cd /tmp wget http://myserver/my-software.swu wget http://myserver/swupdate-public.pem swupdate -i my-software.swu -k swupdate-public.pem
- Note that you can skip the -k param have not enabled signed images via CONFIG_SIGNED_IMAGES in your swupdate.config
Note that if you require support for SWUpdate to complete an install that isn't already there (for example you want to add the capability to update GSC firmware via the gsc_update utility) you will either need to a) add a static linked version of that tool to your image or b) do a 2-stage update where you add the required tools first, then use them in a future update
For Venice
In this example Buildroot branch 2021.08.x was used.
Here are the relevant files to add to your buildroot directory:
- configs/venice_swupdate_defconfig: Buildroot defconfig (represents the minimal 'changes' made to buildroot default config):
cat << EOF > configs/venice_swupdate_defconfig BR2_aarch64=y BR2_KERNEL_HEADERS_5_10=y BR2_ROOTFS_OVERLAY="overlay" BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_CUSTOM_GIT=y BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://github.com/Gateworks/linux-venice.git" BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="v5.10.18-venice" BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="venice_minimal_kernel_defconfig" BR2_LINUX_KERNEL_INSTALL_TARGET=y BR2_PACKAGE_UBOOT_TOOLS=y BR2_PACKAGE_OPENSSL=y BR2_PACKAGE_LIBCONFIG=y BR2_PACKAGE_JSON_C=y BR2_PACKAGE_LIBCURL=y BR2_PACKAGE_SWUPDATE=y BR2_PACKAGE_SWUPDATE_CONFIG="swupdate.config" BR2_TARGET_ROOTFS_EXT2=y BR2_TARGET_ROOTFS_EXT2_4=y BR2_TARGET_ROOTFS_EXT2_GZIP=y EOF
- Note that SWupdate reuqires ZLIB if we are going to use gzip compression
- Download or create a defconfig:
wget http://dev.gateworks.com/buildroot/venice/minimal/venice_minimal_kernel_defconfig
- overlay/etc/fw_env.config: config file for u-boot env tools fw_setenv
mkdir overlay mkdir overlay/etc cat << EOF > overlay/etc/fw_env.config # Device offset Env. size /dev/mmcblk2boot0 0x3f0000 0x8000 /dev/mmcblk2boot0 0x3f8000 0x8000 EOF
- Note that this file is whitespace sensitive
- Create swupdate.config config file to build swupdate executable which drives the update process.
cat << EOF > swupdate.config # We do not need MTD or LUA support # CONFIG_MTD is not set # CONFIG_LUA is not set CONFIG_SIGNED_IMAGES=y CONFIG_ENCRYPTED_IMAGES=y # Suricatta provides support for fetching updates via a Hawkbit server if desired CONFIG_SURICATTA=y CONFIG_SURICATTA_SSL=y CONFIG_SURICATTA_STATE_CHOICE_BOOTLOADER=y # We need the raw handler to image to an MMC partition CONFIG_RAW=y # We need the shellscript handler for our update.sh shellscript CONFIG_SHELLSCRIPTHANDLER=y # We need the bootloader handler to alter the u-boot environment CONFIG_BOOTLOADERHANDLER=y EOF
- Note: The "BR2_PACKAGE_SWUPDATE_CONFIG=" Buildroot config argument could potenially point to an existing file, edit this peramater using menuconfig to point to the file you're creating here.
- sw-description: part of the actual firmware OTA which describes the process and file manifest of the update image. See here for syntax
cat << EOF > sw-description software = { version = "0.1.0"; description = "Firmware update for XXXXX Project"; /* images installed to the system */ images: ( { filename = "venice_swupdate.ext2.gz"; device = "/dev/update"; type = "raw"; sha256 = "b443d32b1f2036d731a423f1c9fd6c05731eb107b48b84bc42be95c0c7b31f03"; compressed = true; } ); scripts: ( { filename = "update.sh"; type = "shellscript"; sha256 = "80463da620c42e02f51b360a33a34d0f180992cbd398c0ea6db0a5d838fa3739"; } ); } EOF
- Note: You will need to generate your own sha256, use the "sha256sum" command to do this. Check that your filename is accurate.
- update.sh: This is the script that SWUpdate runs which we use as both a preinst and psotinst script (via cmdline). We determine the current root device and, flip it, and symlink /dev/update to the device to update to. We don't have to do the image install as we've configured SWUpdate to do that for us in sw-descrption images.
cat << EOF > sw-descrption #!/bin/sh if [ $# -lt 1 ]; then exit 0; fi function get_current_root_device { for i in `cat /proc/cmdline`; do if [ ${i:0:5} = "root=" ]; then CURRENT_ROOT="${i:5}" fi done } # ping-pong between /dev/mmcblk2p1 and /dev/mmcblk2p2 # (adapt for your partitioning scheme and/or root device type) function get_update_part { CURRENT_PART="${CURRENT_ROOT:-/dev/mmcblk2p1}" if [ $CURRENT_PART = "/dev/mmcblk2p1" ]; then UPDATE_PART="2"; else UPDATE_PART="1"; fi } function get_update_device { UPDATE_ROOT=${CURRENT_ROOT%?}${UPDATE_PART} } if [ $1 == "preinst" ]; then # get the current root device get_current_root_device # get the device to be updated get_update_part get_update_device # create a symlink for the update process ln -sf $UPDATE_ROOT /dev/update fi if [ $1 == "postinst" ]; then # get the current root device get_current_root_device # get the device to be updated get_update_part get_update_device # toggle rootpart # we do it twice to write to both primary/secondary env fw_setenv mmcbootpart $UPDATE_PART fw_setenv mmcbootpart $UPDATE_PART fi EOF
- With these files created proceed with the build:
# build buildroot rootfs make venice_swupdate_defconfig make # create a private/public key combo for signature verifcation openssl genrsa -out swupdate-priv.pem # private openssl rsa -in swupdate-priv.pem -out swupdate-public.pem -outform PEM -pubout # public # sign the sw-description openssl dgst -sha256 -sign swupdate-priv.pem sw-description > sw-description.sig
- Add device tree files to rootfs and gzip
mount output/images/rootfs.ext2 /mnt cp output/build/linux-v5.10.18-venice/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7*.dtb /mnt/boot/ umount /mnt cp output/images/rootfs.ext2 ./venice_swupdate.ext2 gzip venice_swupdate.ext2
- Build swupdate image
for i in sw-description sw-description.sig update.sh venice_swupdate.ext2.gz; do echo $i; done | cpio -ov -H crc > my-software.swu
- Note that you can skip the inclusion of sw-description.sig and the pub/priv key if you don't care about signatures and have not define CONFIG_SIGNED_IMAGES in your swupdate.config
- create an MBR partition table that defines LinuxA and LinuxB partitions we will ping-pong between.
# 1: 32768:4194304 (2GiB) LinuxA # 2: 4227072:4194304 (2GiB) LinuxB # 3: 8421376:4194304(2GiB) userdata wget -q https://raw.githubusercontent.com/Gateworks/bsp-newport/sdk-10.1.1.0-newport/ptgen /bin/bash ptgen \ -p 0x83:32768:4194304 \ -p 0x83:4227072:4194304 \ -p 0x83:8421376:4194304 \ > output/images/mbr.bin
- Note: How did we arive at these values? "0x83" is the Linux partition type, "32768" is 16MB (the boot firmware offset) in 512 byte blocks, "4194304" is 2GiB in 512 byte blocks, this will be the length of the partition, "4227072" is the offset plus the length of the partition, this is where the next partion will start.
- Bonus fact, here's a way to calculate these values on the command line:
echo $((16*1024*1024)) # 16MB in bytes echo $((16*1024*1024/512)) # to 512 byte blocks. printf "0x%x\n" $((16*1024*1024)) # to hex
- Boot the board, break out in the bootlader, then update the partition table:
tftpboot ${loadaddr} mbr.bin && mmc dev 2 && mmc write ${loadaddr} 0 1 # mbr is at 0 and 1 sector long
- While in U-Boot install the Buildroot rootfs to the first Linux partition offset (LinuxA)
tftpboot ${loadaddr} rootfs.ext4.gz && gzwrite mmc 2 ${loadaddr} ${filesize} 0x100000 0x1000000 # rootfsA is at 0x1000000 (16MiB)
- In U-Boot set the env to use the mmcbootpart variable that our update.sh will need in order to toggle the partition after a successful update. Custom bootargs will also be applied in this step.
setenv mmcbootpart 1 setenv bootcmd 'setenv bootargs console=${console} root=/dev/mmcblk2p${mmcbootpart} rootwait rw; setenv fsload load mmc 2:${mmcbootpart}; setenv dir /boot; run loadfdt && $fsload ${kernel_addr_r} boot/Image && booti ${kernel_addr_r} - ${fdt_addr_r}' saveenv
- Note the single quotes around the bootargs value as we do not want U-Boot to expand the args until runtime
- After you boot to buildroot you can fetch and install the SWUpdate image with:
# bring up networking udhcpc -i eth0 # fetch image cd /tmp wget http://myserver/my-software.swu wget http://myserver/swupdate-public.pem swupdate -i my-software.swu -k swupdate-public.pem
- Note that you can skip the -k param have not enabled signed images via CONFIG_SIGNED_IMAGES in your swupdate.config
If you would like these files pre-compiled, download them Here.
Other Buildroot Wiki Pages
See also:
Attachments (3)
- 0001-package-add-gst-gateworks-apps.patch (3.0 KB ) - added by 4 years ago.
- 0002-configs-add-various-Gateworks-Ventana-configs.patch (10.6 KB ) - added by 4 years ago.
- 0003-board-gateworks-add-overlay-for-Gateworks-Ventana-bo.patch (17.9 KB ) - added by 4 years ago.
Download all attachments as: .zip