November 29, 2025

Orange Pi R1 Plus LTS (RK3328) - Sources for Debian SD card

When I began working with this board again in 2025, I went looking for a more recent Debian image to place on an SD card.
I found this: He splits the image into two pieces (that you can simply cat together). These are the boot image and the debian image. My interest here is in the boot image. It contains the partition table, chip specific code, ATF, and U-boot.

You can skip the next several sections that document my failed attempt to follow instructions and use Docker. Continue on to the section that says "Forget Docker" and you will see I had success without any of that.

My goal is just to build what he calls the "boot image", which has U-boot and the necessary BL31 image from "arm trusted firmware (ATF)".

Using Docker (did not work)

His instructions to build the boot image are:
docker build -t sd-images https://github.com/johang/sd-card-images.git
mkdir -p /tmp/sd-images
docker run --rm -v /tmp/sd-images:/artifacts sd-images build-boot raspberrypi_3b bcm2837 rpi_3_defconfig aarch64-linux-gnu
I have never had any dealings with "docker", but I suppose we can give it a try. He says the above process will leave the boot image in /tmp/sd-images.

Install Docker

I am running Fedora 42 on an x86 system. I do this:
su
dnf install docker
dnf update
The install removes some selinux files -- which is fine by me, I have selinux disabled.

I do the dnf update simply because things on my system are quite out of date. I will also end up needing to reboot. I get some weird error about Nvidia modules not building properly. I know this has something to do with me installing CUDA development tools on my machine, which have conflicts with the drivers I use to run my system. All this has nothing to do with docker, so we will try to ignore it for now. But it turns ugly. I spend an hour or so getting rid of the entire CUDA package set.

I find this nice tutorial:

Of course all tutorials seem nice at first. I run into my first problem right away:
docker run hello-world
docker: permission denied while trying to connect to the Docker daemon socket
at unix:///var/run/docker.sock
It works as root, which tells me that the docker daemon is running properly. It does go fetch the hello world thing from the "docker hub" for me. Some searching tells me that it is not the best idea to run docker as root. I edit /etc/group and add "tom" to the docker group. I have to log out and back in again for this to take effect. Docker gives me a bunch of tips and suggests trying:
docker run -it ubuntu bash
This pulls an "ubuntu:latest" image from the hub and runs it for me.

Back to SD-sources and Docker

I try the first command that is suggested:
docker build -t sd-images https://github.com/johang/sd-card-images.git
This fails. A look at the error message shows that it is trying to do something with apt-get. I don't know enough to tell if this is simply because I am running on Fedora, or whether it is trying to run things in a debian docker image -- but this is turning into much more of a tangle than I want to dive into. I peek a little more after I clone his repository and see that all this is coming from the "Dockerfile" which seems to specify a ubuntu image. As a Docker beginner, this is above my pay grade.

Forget Docker, try a different approach

cd Projects/RK3328
git clone https://github.com/johang/sd-card-images.git
cd sd-card-images
Here is the line he said would build the boot image for a rpi-3:
docker run --rm -v /tmp/sd-images:/artifacts sd-images build-boot raspberrypi_3b bcm2837 rpi_3_defconfig aarch64-linux-gnu
I squint at this and see the tail end is the command:
build-boot raspberrypi_3b bcm2837 rpi_3_defconfig aarch64-linux-gnu
In "scripts" is a shell script named "build-boot" that extracts and renames 4 arguments:
BOARD_ID = raspberrypi_3b
CHIP_ID = bcm2837
DEFCONFIG = rpi_3_defconfig
TUPLE = aarch64-linux-gnu
It then has a long case/switch on CHIP_ID.

And now for the RK3328

For a chip that matches "rk*", the "build-boot" script invokes:
build-boot-rk "${BOARD_ID}" "${CHIP_ID}" "${DEFCONFIG}" "${TUPLE}"
Stepping back a bit. I grep for "3328" and find the following lines. What are these csv files for? My guess is that he has a top level script that automates the build process for all the boards in his collection.
boards.csv:orangepi_r1_plus_lts,Orange Pi R1 Plus LTS,Xunlong,rk3328,orangepi-r1-plus-lts-rk3328_defconfig,aarch64-linux-gnu,rk3328-orangepi-r1-plus-lts
chips.csv:rk3328,RK3328,Rockchip,ARM Cortex A53,armv8,arm64
Squinting at these lines in the CSV file, I can guess the following:
BOARD_ID = orangepi_r1_plus_lts
CHIP_ID = rk3328
DEFCONFIG = orangepi-r1-plus-lts-rk3328_defconfig
TUPLE = aarch64-linux-gnu
So, I could invoke "build-boot" as:
build-boot orangepi_r1_plus_lts rk3328 orangepi-r1-plus-lts-rk3328_defconfig aarch64-linux-gnu
Poking around yet more I discover docs/_boards/orangepi_r1_plus_lts.md -- This looks like more stuff for his automated scheme and not relevant to what I am doing right now.
# DO NOT EDIT - Generated by rebuild-jekyll-boards
layout: board
title: Orange Pi R1 Plus LTS SD card images
description: "Minimal, pure and up-to-date vanilla Debian/Ubuntu arm64 SD card images for Orange Pi R1 Plus LTS by Xunlong, SoC: Rockchip RK3328, CPU ISA: armv8"
board_id: orangepi_r1_plus_lts
board_dtb_name: rk3328-orangepi-r1-plus-lts
board_name: Orange Pi R1 Plus LTS
board_maker_name: Xunlong
board_soc_name: Rockchip RK3328
board_cpu_name: ARM Cortex A53 (armv8)
board_cpu_arch_isa: armv8
board_cpu_arch_debian: arm64

Tools in "rkbin"

Back in build-boot-rk, I find (more or less):
git clone --depth 1 --reference-if-able https://github.com/rockchip-linux/rkbin.git rkbin
I just do this:
cd Projects/RK3328
mkdir sources
cd sources
git clone https://github.com/rockchip-linux/rkbin.git
The webpage is here. You can ask google chrome to translate the Chinese for you.

Build bl31 in the ATF collection

As build-boot-rk continue along, it will do these two things for the rk3328:
build-atf "${CHIP_ID}" "${TUPLE}"
export BL31="$PWD/arm-trusted-firmware/build/${CHIP_ID}/debug/bl31/bl31.elf"

build-u_boot "${DEFCONFIG}" "${TUPLE}"
The sources are:
https://github.com/ARM-software/arm-trusted-firmware.git
https://source.denx.de/u-boot/u-boot.git/
I clone these for now. I am somewhat surprised that the "official" sources for each of these is what is used -- and I am pleased.
Details of how each is built can be found in each build script.
The ATF build is like so:
cd arm-trusted-firmware
make PLAT="rk3328" DEBUG=1 bl31

I do this, and it just works with no errors or hassles and the build takes less than a minute.

Apparently PLAT is short for "platform".

Build U-boot itself

From their script, this requires this:
DEFCONFIG = orangepi-r1-plus-lts-rk3328_defconfig
TUPLE = aarch64-linux-gnu
export BL31="$PWD/arm-trusted-firmware/build/${CHIP_ID}/debug/bl31/bl31.elf"
build-u_boot "${DEFCONFIG}" "${TUPLE}"
Or, in other words:
export BL31="$PWD/arm-trusted-firmware/build/rk3328/debug/bl31/bl31.elf"
build-u_boot orangepi-r1-plus-lts-rk3328_defconfig aarch64-linux-gnu
Note that the BL31 environment variable is set to tell U-boot where to find the bl31.elf image.

On my system, this will boil down to:

export BL31="/Projects/OrangePi/r1plus/sources/arm-trusted-firmware/build/rk3328/debug/bl31/bl31.elf"
export CROSS_COMPILE=aarch64-linux-gnu-
export LOCALVERSION="trebisky"
cd u-boot
make clean
make orangepi-r1-plus-lts-rk3328_defconfig
make
I get an error about a missing thing called "swig". This seems to be in relation to Python. On my Fedora system I try:
su
dnf install swig
make
On to the next error, it is complaining that the C compiler cannot find Python.h There is some question of whether we need to specify python2 or python3, but I just do:
dnf install python-devel
make
On we go to another error. Apparently "engine" is deprecated as part of openssl, but lucky for us Fedora keeps a package for it anyway.
fatal error: openssl/engine.h: No such file or directory

dnf install openssl-devel-engine
make
On we go to:
fatal error: gnutls/gnutls.h: No such file or directory

dnf install gnutls-devel
make
It now goes a long ways, compiling all kinds of source files, but ends with this:
binman: Node '/binman/simple-bin/fit': subnode 'images/@atf-SEQ': Failed to read ELF file: Python: No module named 'elftools'

dnf install python3-pyelftools
The build ends with this strange message:
Image 'simple-bin' is missing optional external blobs but is still functional: tee-os
/binman/simple-bin/fit/images/@tee-SEQ/tee-os (tee-os):
   See the documentation for your board. You may need to build Open Portable
   Trusted Execution Environment (OP-TEE) and build with TEE=/path/to/tee.bin
Indeed this message comes out after the generation of "u-boot-rockchip.bin" so I am just going to ignore this "optional external blob".

The end game

When build-boot-rk is done, it does:

# Copy U-Boot to 64 sectors from start
dd if=u-boot/u-boot-rockchip.bin of=tmp.img seek=64 conv=notrunc
Then back in build-boot, this happens:
truncate -s 32M tmp.img
pigz tmp.img
I end up with this:
-rw-r--r--  1 tom tom 550328 Dec  6 18:26 tmp.img.gz
-rw-r--r--  1 tom tom 540864 Nov 26 08:30 boot-orangepi_r1_plus_lts.bin.gz
My file is slightly bigger than what I downloaded 10 days ago, but it looks reasonable. Let's put it onto an SD card and try it.
unpigz tmp.img.gz
dd if=tmp.img of=/dev/sdc bs=64M
The image is only 34M in size, so the copy is one partial record with this big blocksize.

And it works!. I even get to see my name on the version line.

U-Boot 2026.01-rc3trebisky-00026-g31bf4a1c3087 (Dec 06 2025 - 18:19:51 -0700)
Finally, I customize the U-boot environment to do the usual network boot I like to use:
setenv linux bootflow scan
setenv loadaddr 0x02000000
setenv kyu echo Booting Kyu via dhcp/tftp\; dhcp\; go \${loadaddr}
setenv bootcmd run kyu
saveenv
It all works just fine.
Have any comments? Questions? Drop me a line!

Tom's electronics pages / tom@mmto.org