Getting Started

Install dependencies

Debian 12 / Ubuntu 22.04

sudo apt update
sudo apt install -y git qemu-user-static binfmt-support

# Podman (recommended)
sudo apt install -y podman podman-docker
sudo touch /etc/containers/nodocker
# Docker
#sudo apt install docker.io

# Optional dependcies for minor features
sudo apt install -y systemd-container

Check out the code

bsp uses git submodules. As such, please check out the code with the following command:

git clone --recurse-submodules https://github.com/radxa-repo/bsp.git

Understand bsp's usage

Once the repo is cloned on your machine, you can run bsp to check the built-in help:

Tip

The built-in help is intended as the first step to troubleshoot for bsp usage, while the documentation site covers more about high level applications and low level implementation details. As such, you are more likely to find your questions solved by an option listed in the built-in help.

It is strongly recommended to read the built-in help at once, if you want to do anything beyond the trival package rebuild.

cd bsp
./bsp

Build your first packages

bsp now supports building kernel and U-Boot in a single step, and specify them by product name instead of specific profiles. This should help new users to test their setup without much bsp knowledge:

# The following command will build linux-stable
# and u-boot-latest for radxa-zero
./bsp radxa-zero

Custom kernel/firmware developer should at least read the Development reference section to better understand the development workflow and the internal data sctructure for bsp.

Local development workflow

In this article, we will demonstrate how to develop and create patches for bsp.

Prepare working tree

Use --no-build and -C|--distclean to set up the source tree with bsp predefined profiles:

./bsp -C --no-build linux latest

Retrive additional commit history

bsp uses shallow clone by default to save bandwidth and speed up build process. However, you might want to access additional git history for rebase or cherry-pick. You can use either of the following 2 options to fetch more commits:

cd .src/linux
# Fetch HEAD~10 commits
git fetch --depth=10 --all
# Fetch everything
# WARNING! VERY SLOW!
git fetch --unshallow --all

Build packages

You can use --dirty to build without resetting the source tree. This will allow the build process to reuse previously built objects:

./bsp --dirty linux latest

Install build artifacts

You can use ./bsp install to install the built package to a root partition (like a microSD card):

./bsp install linux-image-6.*_arm64.deb /dev/sdb2

Create patch set

After you are happy with the changes, you can export your commits with git format-patch:

mkdir -p linux/latest/0500-my-first-pr
cd .src/linux
# Update start-number to the last one + 1
# in your targetting patch subfolder
git format-patch -M -N --start-number 1 --zero-commit -o ../../linux/latest/0500-my-first-pr HEAD~1

Move the outputted patch files to the corresponding subfolder, and you are ready to submit your first PR!

Developer FAQ

bsp takes a lot of time applying patches

There are 2 possible causes:

  1. Your bsp project is stored on mechinical hard drives.
    As patching is random 4K intensive, we suggest you put bsp on solid state drives.
  2. Your source tree has too many dangling commits.
    This happens naturally as you use the project. You can go to the source tree under .src and run git gc --prune to restore the performance.

Understand profile

Profiles are subfolders under linux and u-boot folder. Each of them describes a specific flavor of their targeted software.

Special files

Tree structure and mode of operation

sample
├── 0001-common -> ../.common/
├── 0010-backport
│   ├── 0001-scripts-dtc-Update-to-upstream-version-v1.6.0-51-g18.patch
│   ├── 0002-kbuild-Add-support-to-build-overlays-.dtbo.patch.ignore
│   ├── 0003-mm-page_alloc-fix-building-error-on-Werror-array-com.patch
│   └── 0004-etherdevice-Adjust-ether_addr-prototypes-to-silence-.patch
├── 0001-Fix-multiple-definition-of-yylloc.patch
├── 0002-Suppress-additional-warnings.patch
├── fork.conf
├── kconfig.conf
└── rockchip -> ../.rockchip

fork.conf

fork.conf is the profile configuration file. This file is mandatory and is sourced early by bsp.

BSP_GIT defines upstream source repo.

BSP_COMMIT or BSP_BRANCH or BSP_TAG defines the exact source code. The first defined option in the listed order will be used.

BSP_DEFCONFIG defines the defconfig used for building. Default to defconfig.

SUPPORTED_BOARDS defines the supported product list, which is a Bash array. bsp will use this list to create metapackages that reference the binary package. Suffixes are commonly used to denote kernel/firmware variants that also support the same board. However, the default package is the one that matches exactly to the product name.

You can read more about SUPPORTED_BOARDS in Build artifacts section.

kconfig.conf

kconfig.conf is a kernel configuration file. This file mirrors defconfig format.

Build artifacts

Every time bsp completes a build, it will create at least 2 packages. We will discuss what they are in this article.

linux-rockchip 5.10.110-5 release will be used as an example.

Specifically, we will focus on the following 3 packages:

File NameSize
linux-headers-5.10.110-5-rockchip_5.10.110-5-1932709cf_arm64.deb7.4 MB
linux-headers-radxa-nx5-io_5.10.110-5-3a557f6_all.deb1.1 KB
......
linux-headers-rockchip_5.10.110-5-3a557f6_all.deb1.08 KB

Debian package naming convention

Generall, Debian packages are named in the following format:

PACKAGE-NAME _ PACKAGE-VERSION _ PACKAGE-ARCHITCTURE .deb

The three components are seperated by the underscore.

Binary package

Binary package is the artifacts generated by the related source code.

They are large because they contain the real code.

In the above list linux-headers-5.10.110-5-rockchip is the real package.

For kernel related package, we additionally adds part of the package version to the package name, which is why you see 5.10.110-5 repeated in the package name above. The advantage of this is that now different versions of kernel are actually different packages (remember they are identified by the package name). This allows us to install different kernels at the same time, and the user can decided which one to boot at run time.

For firmware package, we only allow a single package to be installed (i.e. no version in the name). This is because firmware has to be installed in a fixed place, so it is not possible to switch between them.

In above list you also see 1932709cf is included in the package version. This is the base commit ID in case the related bsp profile is defined with a Git branch. This way we can determine which exact commit was used at the time of building.

Metapackages

One issue introduced by having a unique name for each kernel package is that now it is very hard to track which kernel supports what products, and there is no automatic upgrade since each new version is an entire new package.

To solve this problem, metapackages are built by bsp along with the binary package.

The list of metapackages is based on SUPPORTED_BOARDS defined in fork.conf. The purposes for them are:

  1. They reference the latest binary package as their dependency.
  2. Since they do not have a unique name per release, they can be updated, which will automatically pulls the new binary package as the dependency.
  3. They also have a consistent name, so this makes tracking kernel with product a non-issue.

Beyond the supported products defined in SUPPORTED_BOARDS, we also define the profile name as a metapackage. This allows the user to install a specific profile of kernel/firmware instead of worrying about product updates it's source code dependencies.

Metapackages are usually very small since they only need to reference the binary package, and contains no code.

In above list you also see 3a557f6 is included in the package version. Since they are created entirely by bsp, this is the bsp commit used at the build time.

Firmware management

Firmware, or do you mean U-Boot?

bsp is intended to be a general Board Support Package builder. In this sense, there are firmwares which perform low level initialization, and kernel which runs the operating system. Currently the only kernel bsp supports is Linux, and the only supported firmware is U-Boot. However, we plan to support EDK2 in the future, thus this section is titled firmware management.

U-Boot installation location

Following Debian convention, we install U-Boot under /usr/lib/u-boot folder, and save all supported bootloaders in a single package.

We also provide a maintenance script called setup.sh.

Extracting binaries from released package

Firmware binaries are usually needed to perform various offline recovery tasks. As we only release firmware in the form of Debian package, it is necessary to extract the required files first before performing any recovery steps.

On Windows, you can extract .deb files with 7-Zip.

On Linux, you can run following command to extrace the package:

ar p u-boot-latest.deb data.tar.xz | tar -xJ

setup.sh usage

Under each board's directory, there are a few binary blobs, along with a maintenance script called setup.sh, which provides a wrapper for common management tasks.

There is minimal error checking in the script, as such, it is intended as the advanced method of bootloader management. Normal user should use rsetup instead, which provides a TUI for bootloader update.

Additionally, not all commands are intended for the end user usage. Below are documented and supported commands:

Common setup.sh command

setup.sh maskrom

Maskrom is a special boot mode, that can run binary passed via USB OTG connection. This is usually the first command to run when you boot the board in maskrom mode.

For Amlogic devices, the binary is actually a normal U-Boot binary running in memory, so you can access the U-Boot console for recovery.

For Rockchip devices, the binary allows access to on-board eMMC via rkdeveloptool.

setup.sh update_bootloader <file>

This command update the installed bootloader on a uncompressed system image file. Since block devices is also a file, the command can also update the bootloader on storage medias like eMMC or NVMe SSD.

Rockchip specific setup.sh command

setup.sh maskrom_autoupdate_bootloader

This command update the eMMC bootloader via Rockchip Maskrom mode. You do not need to run setup.sh maskrom first when using this command.

setup.sh maskrom_autoupdate_spinor

This command update the SPI bootloader via Rockchip Maskrom mode. You do not need to run setup.sh maskrom first when using this command.

setup.sh update_spinor [mtd_device]

This command update the installed bootloader on the SPI flash. When no mtd_device is assigned, it will update /dev/mtd0.

Update firmware

Download and extract prebuilt firmware

If you do not want to build the firmware from source, you can also use our prebuilt firmwares.

Please first go to their latest release and find your product in the asset list:

Example release

Example release from https://github.com/radxa-pkg/u-boot-latest/releases/latest

You should then download the binary package from the supported release. In our example we will use u-boot-latest_2023.07.02-6-4257d241_arm64.deb as an example.

Tip

If you don't know what is binary package, please read Build artifacts section.

mkdir extract
cd extract
wget https://github.com/radxa-pkg/u-boot-latest/releases/download/2023.07.02-6/u-boot-latest_2023.07.02-6-4257d241_arm64.deb
ar vx *.deb
tar xvf data.tar.xz
ls usr/lib/u-boot/

You should see output similar to this:

[excalibur@yuntian extract]$ ls usr/lib/u-boot/
radxa-cm3-io          radxa-e23  radxa-zero       rock-3a  rock-4-core-io  rock-pi-4a       rock-pi-4b-plus  rock-pi-s
radxa-cm3-rpi-cm4-io  radxa-e25  radxa-zero-2pro  rock-3b  rock-4c-plus    rock-pi-4a-plus  rock-pi-4c
radxa-cm3-sodimm-io   radxa-e61  radxa-zero3      rock-3c  rock-4se        rock-pi-4b       rock-pi-n10

This means the firmware is extracted and is ready to use.

Update firmware existing system

To install the firmware to an existing system image (must be uncompressed raw image), or a block device (ex. microSD card), you can use the included setup.sh. With ROCK 4SE as an example:

sudo usr/lib/u-boot/rock-4se/setup.sh update_bootloader ~/system.img # Update a system image
# OR
sudo usr/lib/u-boot/rock-4se/setup.sh update_bootloader /dev/sdX # Update a block device

You can read more abour setup.sh from its own page.

Running from GitHub Workflows

Please check out our GitHub workflows as an example.

Reproduce a previous release

In this example, we will recreate linux-rockchip 5.10.110-5 release.

  1. Follow Build artifacts section, We now have the following essential commit information at the release build time:
    radxa/kernel: 1932709cf
    bsp: 3a557f6

  2. However, fork.conf only takes full commit hash. So click the radxa/kernel link above, and click Browse files button on GitHub page, that will get you the full commit hash in the URL bar: radxa/kernel: 1932709cf7d98d0d92952bba38d990d938fabc58

  3. We can also check the content of overlay.sh to find the commit used for radxa/overlays:
    radxa/overlays: 4940ae33e4def0fb9133faf68adf0c3421b61f06

  4. We can now recreate the package:

git clone --recurse-submodules https://github.com/radxa-repo/bsp.git
cd bsp
# switch to bsp commit found above
git switch --detach 3a557f6
# replace BSP_BRANCH with radxa/kernel commit found above
sed -i "s/BSP_BRANCH.*/BSP_COMMIT='1932709cf7d98d0d92952bba38d990d938fabc58'/" linux/rockchip/fork.conf
./bsp linux rockchip
# the prepared kernel tree will be available under .src/linux