PINE64 ROCKPro64/Installing U-Boot
There are a number of places you can find pre-built images to boot your ROCKPro64. This guide describes how to set up Das U-Boot bootloader on ROCKPro64 from mainline upstream source code.
Prerequisites
U-Boot can be built on an ARM64 device or cross-compiled on a PC. Regardless, we will need:
- git, to check out the sources
- dtc, to compile device trees
- swig, to build U-Boot
- crossdev, since both a 64- and 32-bit ARM toolchains are required
Let's make sure these are installed:
root #
emerge --ask --update dev-vcs/git sys-apps/dtc dev-lang/swig sys-devel/crossdev
Please review the crossdev article if you don't already have the it set up.
Install the 32-bit toolchain. This is required to build code for Cortex-M0 MCUs in BL31:
root #
crossdev --target arm-none-eabi
If cross-compiling, install the 64-bit cross-compiler toolchain:
root #
crossdev --target aarch64-unknown-linux-gnu
Compiling
Build BL31
Before U-Boot, we need to build the BL31 ("Boot Loader stage 3-1") firmware.
Get the sources:
user $
git clone https://github.com/ARM-software/arm-trusted-firmware.git
user $
cd arm-trusted-firmware
user $
git tag
v2.7.0
Let's select the latest currently stable version:
user $
git checkout v2.7.0
The repository contains a few binaries. If this is unacceptable, they may be deleted:
user $
git rm '*.bin'
rm 'plat/arm/board/common/protpk/arm_protpk_rsa_sha256.bin' rm 'plat/arm/board/common/rotpk/arm_rotpk_ecdsa_sha256.bin' rm 'plat/arm/board/common/rotpk/arm_rotpk_rsa_sha256.bin' rm 'plat/rockchip/rk3368/drivers/ddr/rk3368_ddr_reg_resume_V1.05.bin' rm 'plat/rockchip/rk3399/drivers/dp/hdcp.bin'
To compile on ARM64, run:
user $
make -j$(nproc) PLAT=rk3399 bl31
If cross-compiling, run:
user $
make -j$(nproc) PLAT=rk3399 CROSS_COMPILE=aarch64-unknown-linux-gnu- bl31
Once finished, make a copy of resulting bl31.elf
binary for safekeeping:
user $
mkdir ~/rk3399-atf-2.7.0
user $
cp build/rk3399/release/bl31/bl31.elf ~/rk3399-atf-2.7.0
Build U-Boot
Now we're ready to build U-Boot itself. Let's get the sources:
user $
git clone https://source.denx.de/u-boot/u-boot.git
user $
cd u-boot
user $
git tag
v2022.07
Check out latest currently stable version:
user $
git checkout v2022.07
Instead of cloning the complete u-boot repository as shown in the steps above, you may save some time and disc space by cloning branch v2022.07 only:
user $
git clone --depth 1 --branch v2022.07 https://source.denx.de/u-boot/u-boot.git
Load ROCKPro64-specific default configuration:
user $
make rockpro64-rk3399_defconfig
Similar to the Linux kernel, menuconfig
can be used to make changes to the configuration. You don't need to make any changes.
user $
make menuconfig
To compile on ARM64, run:
user $
make -j$(nproc) BL31=~/rk3399-atf-2.7.0/bl31.elf
If cross-compiling, run:
user $
make -j$(nproc) BL31=~/rk3399-atf-2.7.0/bl31.elf CROSS_COMPILE=aarch64-unknown-linux-gnu-
Make sure
BL31
is pointing to the bl31.elf
built earlier!Installing
When powered on, ROCKPro64 will attempt to find a bootloader in the following order:
- Onboard 128MB SPI Flash
- Removable eMMC
- microSD card
Once U-Boot is loaded from either of these locations, it could then be used to load the kernel from either of these, from peripherals connected through SATA, USB or NVMe, or even through the network.
Installing on microSD/eMMC
As root, enter the u-boot build directory, and write the images to the card.
* Note that
u-boot.itb
is written starting at sector 16384. Make sure your card is partitioned such that there's no filesystem there.
- Be sure to replace
/dev/mmcblk1
with the correct device!
root #
cd /path/to/u-boot
root #
dd if=idbloader.img of=/dev/mmcblk1 seek=64
root #
dd if=u-boot.itb of=/dev/mmcblk1 seek=16384
Observing boot message with serial console
Before installing a Linux operating system, you may want to ensure that U-Boot is booting correctly. If you have a USB-to-TTL connector (such as this one or this one), connect it to pins 6 (GND), 8 (UART2_TX) and 10 (UART2_RX) on the PI2 Bus (consult the Pine64 RockPro Wiki for reference) to see the early boot up messages:
root #
screen /dev/ttyUSB0 1500000
* The Pine64 Wiki indicates that the device may not start if pin 10 is connected before powering on
- Make sure RX and TX are not backwards
Optional: Adapting baud rate
Serial communication usually requires both sides of the communication to agree on a common baud rate. By default, Rockchip uses 1500000 bauds, which may be too fast for common USB-to-serial adapters. As a consequence, you may not see any boot-messages from u-boot. A much more common and widely supported, but slower baud rate is 115200 bauds. U-Boot can be configured to use this slower baud rate.
Use menuconfig
as described in #Build_U-Boot and reconfigure the symbol BAUDRATE
to 115200
. Rebuild u-boot and copy it to the SD-card. Now start your program of choice for serial communication
root #
screen /dev/ttyUSB0 115200
The Pine64 RockPro64 Wiki gives additional information on serial communication.
Expected boot messages
Now put the card into the device and power on. If successful, you should be able to see output like this (click to expand):
U-Boot TPL 2022.07 (Jul 11 2022 - 20:15:18) Channel 0: LPDDR4, 50MHz BW=32 Col=10 Bk=8 CS0 Row=16/15 CS=1 Die BW=16 Size=2048MB Channel 1: LPDDR4, 50MHz BW=32 Col=10 Bk=8 CS0 Row=16/15 CS=1 Die BW=16 Size=2048MB 256B stride lpddr4_set_rate: change freq to 400000000 mhz 0, 1 lpddr4_set_rate: change freq to 800000000 mhz 1, 0 Trying to boot from BOOTROM Returning to boot ROM... U-Boot SPL 2022.07 (Jul 11 2022 - 20:15:18 -0400) Trying to boot from MMC2 U-Boot 2022.07 (Jul 11 2022 - 20:15:18 -0400) SoC: Rockchip rk3399 Reset cause: POR Model: Pine64 RockPro64 v2.1 DRAM: 3.9 GiB PMIC: RK808 Core: 305 devices, 33 uclasses, devicetree: separate MMC: mmc@fe310000: 3, mmc@fe320000: 1, mmc@fe330000: 0 Loading Environment from SPIFlash... SF: Detected gd25q128 with page size 256 Bytes, erase size 4 KiB, total 16 MiB *** Warning - bad CRC, using default environment In: serial Out: serial Err: serial Model: Pine64 RockPro64 v2.1 Net: eth0: ethernet@fe300000 starting USB... Bus usb@fe380000: ehci_generic usb@fe380000: Failed to get clocks (ret=-19) Port not available. Bus usb@fe3a0000: USB OHCI 1.0 Bus usb@fe3c0000: ehci_generic usb@fe3c0000: Failed to get clocks (ret=-19) Port not available. Bus usb@fe3e0000: USB OHCI 1.0 Bus usb@fe800000: Register 2000140 NbrPorts 2 Starting the controller USB XHCI 1.10 Bus usb@fe900000: Register 2000140 NbrPorts 2 Starting the controller USB XHCI 1.10 scanning bus usb@fe3a0000 for devices... 1 USB Device(s) found scanning bus usb@fe3e0000 for devices... 1 USB Device(s) found scanning bus usb@fe800000 for devices... 1 USB Device(s) found scanning bus usb@fe900000 for devices... 1 USB Device(s) found scanning usb for storage devices... 0 Storage Device(s) found Hit any key to stop autoboot: 0
* Early output from U-Boot TPL/SPL won't be shown on HDMI, only on serial
- You can press a key to interrupt the boot process and enter U-Boot's interactive shell
- You can find the list of commands and their syntax if you run
help
Installing on SPI flash
The other option is to install U-Boot onto the SPI Flash chip that's on the device itself. This is useful if you want to be able to run the system off of other media without the eMMC or the microSD present.
We need to create a new SPI-specific image. In the u-boot build directory, run:
user $
./tools/mkimage -n rk3399 -T rkspi -d tpl/u-boot-tpl.bin:spl/u-boot-spl.bin idbloader-spi.img
Now, copy the required files to the bootable microSD/eMMC we created earlier:
root #
cp idbloader-spi.img u-boot.itb /mnt/card
Pre-flight checks
ROCKPro64 will attempt to boot from SPI Flash as first priority. If a bad image is flashed, you will need to disable SPI Flash in order to boot from eMMC or microSD!
Before proceeding, let's be sure we can unbrick the device if something goes wrong.
Make sure you have a:
- microSD (or eMMC) with working U-Boot
- working serial adapter to see early boot messages
- jumper cable to connect two adjacent pins
SPI Flash can be disabled by grounding the SPI clock. Unplug the device from power and connect pin 23 (SPI1_CLK) to pin 25 (GND) on the PI2 Bus. Connect to UART, and insert the bootable card. Then, power on the device and press a key to interrupt boot:
Now that you're in the U-Boot prompt, scan for SPI Flash. It should not be available:
=>
sf probe
jedec_spi_nor flash@0: unrecognized JEDEC id bytes: ff, ff, ff Failed to initialize SPI flash at 1:0 (error 0)
Now, disconnect pins 23 and 25 and probe again:
=>
sf probe
SF: Detected gd25q128 with page size 256 Bytes, erase size 4 KiB, total 16 MiB
To wipe SPI Flash, you can run:
=>
sf erase 0 400000
SF: 4194304 bytes @ 0x0 Erased: OK
ROCKPro64's SPI flash has about 100,000 erase cycles per sector and no wear leveling
Write to SPI flash
Boot using microSD/eMMC created earlier and interrupt boot to enter the U-Boot shell.
Substitute mmc 1:1
below with the device/partition number containing your images. mmc 1:1
is the first partition on the microSD. For example, the third partition on eMMC would be mmc 0:3
.
Run the following commands to flash the images:
=>
sf probe
=>
load mmc 1:1 $kernel_addr_r idbloader-spi.img
=>
sf erase 0 +$filesize
=>
sf write $kernel_addr_r 0 $filesize
=>
load mmc 1:1 ${kernel_addr_r} u-boot.itb
=>
sf erase 0x60000 +$filesize
=>
sf write $kernel_addr_r 0x60000 ${filesize}
To reboot, run:
=>
reset
Output will be similar to booting from microSD/eMMC as above, except in the serial console you should see:
Trying to boot from SPI
Booting a Linux system
Now that we have U-Boot installed, next step is to boot the Linux system.
By default, U-Boot scans the partitions of each device (except SATA, see next section) to find something it knows how to load. The default supported file systems are ext2
, ext4
and FAT
, but there are a few others you can enable in U-Boot's menuconfig
. You will want your /boot partition to be formatted with one of those filesystems.
Creating boot.scr
One thing it can load boot.scr
, which will contain commands for U-Boot to execute. This will be in your /boot partition along with your kernel and device tree binary.
First, create a file boot.cmd
:
echo "Loaded boot.scr!"
if test -z "$bootargs"; then
setenv bootargs root=/dev/mmcblk1p2 rootdelay=2
fi
echo "Loading device tree from ${devtype} ${devnum}:${distro_bootpart}..."
load ${devtype} ${devnum}:${distro_bootpart} ${fdt_addr_r} rk3399-rockpro64.dtb
echo "Loading kernel from ${devtype} ${devnum}:${distro_bootpart}..."
load ${devtype} ${devnum}:${distro_bootpart} ${kernel_addr_r} Image.gz
echo "Booting with arguments: ${bootargs}"
booti ${kernel_addr_r} - ${fdt_addr_r}
Be sure to change
root=
to point to your actual root partition!Then, compile it to boot.scr
:
root #
mkimage -C none -A arm64 -T script -d boot.cmd boot.scr
*
mkimage
can be used from either the bin directory in your U-Boot build directory or from dev-embedded/u-boot-tools
- Create a Makefile or a shell script so you don't have to look up the command above next time you need to edit
If the /boot partition is on eMMC or microSD, it should work without additional steps.
Booting off of SATA
Currently, U-Boot will not try to boot from SATA automatically. If you want your /boot partition to be in SATA, you will need to perform some additional steps.
Manually
Expand below to see a sequence of commands to boot from SATA. This is mainly for educational purposes:
=> pci BusDevFun VendorId DeviceId Device Class Sub-Class _____________________________________________________________ 00.00.00 0x1d87 0x0100 Bridge device 0x04 01.00.00 0x1b4b 0x9215 Mass storage controller 0x06 => scsi scan scanning bus for devices... Target spinup took 0 ms. Target spinup took 0 ms. Target spinup took 0 ms. SATA link 3 timeout. AHCI 0001.0000 32 slots 4 ports 6 Gbps 0xf impl SATA mode flags: 64bit ncq led only pmp fbss pio slum part sxs Device 0: (0:0) Vendor: ATA Prod.: CT1000MX500SSD1 Rev: M3CR Type: Hard Disk Capacity: 953869.7 MB = 931.5 GB (1953525168 x 512) Device 1: (1:0) Vendor: ATA Prod.: WDC WD10EARS-00Y Rev: 80.0 Type: Hard Disk Capacity: 953869.7 MB = 931.5 GB (1953525168 x 512) Device 2: (2:0) Vendor: ATA Prod.: HGST HTS725050A7 Rev: GH2O Type: Hard Disk Capacity: 476940.0 MB = 465.7 GB (976773168 x 512) => part list scsi 0 Partition Map for SCSI device 0 -- Partition Type: EFI Part Start LBA End LBA Name Attributes Type GUID Partition GUID 1 0x00000800 0x000207ff "" attrs: 0x0000000000000000 type: 0fc63daf-8483-4772-8e79-3d69d8477de4 guid: dc920591-9516-ed4f-b4b0-67262ae9b42c 2 0x00020800 0x080207ff "" attrs: 0x0000000000000000 type: 0fc63daf-8483-4772-8e79-3d69d8477de4 guid: 82067900-d641-984b-a399-799968d9a850 3 0x08020800 0x0c0207ff "" attrs: 0x0000000000000000 type: 0fc63daf-8483-4772-8e79-3d69d8477de4 guid: b574080b-f70c-0840-b5ab-aaee17da76d6 => ls scsi 0:1 / <DIR> 1024 . <DIR> 1024 .. <DIR> 12288 lost+found 705 boot.cmd 777 boot.scr 60690 rk3399-rockpro64.dtb 8020424 Image.gz <DIR> 1024 u-boot-2022-07 370 Makefile => load scsi 0:1 $fdt_addr_r rk3399-rockpro64.dtb 60690 bytes read in 3 ms (19.3 MiB/s) => load scsi 0:1 $kernel_addr_r Image.gz 8020424 bytes read in 131 ms (58.4 MiB/s) => setenv bootargs root=/dev/sda2 rootdelay=2 => booti $kernel_addr_r - $fdt_addr_r Uncompressing Kernel Image Moving Image from 0x2080000 to 0x2200000, end=3410000 ## Flattened Device Tree blob at 01f00000 Booting using the fdt blob at 0x1f00000 Loading Device Tree to 00000000f1f09000, end 00000000f1f1ad11 ... OK Starting kernel ... [ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034] [ 0.000000] Linux version 5.18.12 (larry@barn) (aarch64-unknown-linux-gnu-gcc (Gentoo 12.1.1_p20220625 p8) 12.1.1 20220625, GNU ld (Gentoo 2.38 p4) 2.38) #17 SMP PREEMPT Sat Jul 16 10:38:56 EDT 2022 ...
Update existing environment script
A better way would be to update the boot script in the existing environment, so that it boots automatically. You can see it if you run
=>
printenv
U-Boot's hush
shell is somewhat reminiscent of a minimal UNIX shell. It does not have a concept of functions, but it can evaluate the values stored in variables using the run
command. The entry point is distro_bootcmd
. You can follow from there to see the steps it takes to boot.
In a similar style, let's add steps to boot from the first disk, and prepend the new scsi0
to boot_targets
:
=>
setenv bootcmd_scsi0 "devnum=0; run boot_pci_enum; run scsi_boot"
=>
setenv scsi_boot "scsi scan; if scsi dev ${devnum}; then devtype=scsi; run scan_dev_for_boot_part; fi"
=>
setenv boot_targets "scsi0 mmc1 mmc0 nvme0 usb0 pxe dhcp sf0"
=>
saveenv
Saving Environment to SPIFlash... Erasing SPI flash...Writing to SPI flash...done OK
Now, if you have your boot partition set up (e.g. it contains boot.scr
), you should be able to boot:
=>
boot
Accessing U-Boot environment from Linux
You can view and modify U-Boot's environment without having to boot into U-Boot's interactive shell.
root #
emerge --ask --update dev-embedded/u-boot-tools
Edit /etc/fw_env.config and replace the provided examples with the following:
# MTD device name Device offset Env. size Flash sector size Number of sectors
/dev/mtd0 0x3f8000 0x8000 0x2000
root #
fw_printenv
altbootcmd=setenv boot_syslinux_conf extlinux/extlinux-rollback.conf;run distro_bootcmd arch=arm baudrate=1500000 board=rockpro64_rk3399…
Be sure to check the first line of the output — if the first line says
Warning: Bad CRC, using default environment
, either you've never modified or saved the environment, or your fw_env.config has wrong valuesTo set a value, run:
root #
fw_setenv test_var 'test value'