[BPI-R4] frank-w BPI-R4 u-boot Build & Flash (NAND)

This post is a supplement to [BPI-R4] How to use frank-w u-boot to boot from Nvme on BPI-R4.
Because NAND u-boot does not support uEnv.txt.
If we want to modify u-boot menu, we need to recompile u-boot and flash it to NAND Flash.

Environmental Information:
Hardware: BananaPi R4 8GB RAM
eMMC: OpenWRT u-boot & OpenWRT
SD: frank-w u-boot & frank-w Debian
NAND(Target): frank-w u-boot
Nvme: Nvme SSD 128GB on M.2 KeyM slot
Compile Host: x86 Debian 12

Partition:
SD Partition:
/dev/sda5 vfat BPI-BOOT
/dev/sda6 ext4 BPI-ROOT

Nvme Partition:
/dev/nvmen1p0 vfat
/dev/nvmen1p1 ext4

After the u-boot is compiled, it will generate u-boot.bin.
But BPI-R4 requires two files, bl2.img and fip.bin, so we need to convert u-boot.bin into bl2.img and fip.bin.

We need to download 2 packages of code to compile frank-w u-boot.
First, download 2025-01-bpi Release u-boot Source Code.
Second, git u-boot branch mtk-atf-2025.

u-boot Source Code:
u-boot-CI-BUILD-2025-01-bpi-2025.01-2025-04-26_0808

mtk-atf-2025:
u-boot

We divide the process into 2 stages, the first stage is to Compile u-boot, and the second stage is to Flash NAND Flash.

There are two goals for compiling u-boot.
The first goal is to set up compilation for BPI-R4, NAND and 8Gb RAM.
The second goal is to adjust the boot menu, specify the default boot number as Nvme and specify the Kernel Name.

Compile u-boot:

  1. Modify build.conf for BPI-R4, NAND and 8Gb RAM
board=bpi-r4
device=spi-nand
extraflags=DDR4_4BG_MODE=1

  1. Modify uEnv_r4.txt for Nvme boot
    bootmenu_default=3 => Base 0, option 4 set number to 3
    askbootnvme=run usenvme; if test “$device” = “nvme”; then setenv fit ${fit};run newboot;fi

  1. Build u-boot
bash ./build.sh importconfig
bash ./build.sh build
bash ./build.sh rename

  1. Copy u-boot image to mtk-atf-2025
    cp u-boot-r4_2025.01–arm64-spi-nand.bin …/u-boot/u-boot.bin.xz => Here is .bin.xz

  2. Modify build.conf in mtk-atf-2025

board=bpi-r4
device=spi-nand
extraflags=DDR4_4BG_MODE=1

image

  1. Convert u-boot.bin to bl2.img and fip.bin
    bash ./build.sh build

  2. Copy bl2.img and fip.bin to SD
    The two files bl2.img and fip.bin will be generated in build/mt7988/release/.
    We put bl2.img and fip.bin to SD vfat Partition, /dev/sda5/.

Flash NAND Flash:
We plug a USB SD card reader into the BPI-R4 USB and use the USB SD card reader as the file source.

  1. Switch to eMMC Boot and boot into u-boot menu

  1. Enable USB
    usb start

  1. Flash u-boot from USB Partition 5
mtd erase spi-nand0
load usb 0:5 $loadaddr bl2.img
mtd write spi-nand0 $loadaddr 0x0 0x100000
load usb 0:5 $loadaddr fip.bin
mtd write spi-nand0 $loadaddr 0x580000 0x200000

image

  1. Switch boot from NAND
    Check RAM Size

EMI: DDR4 4BG mode
EMI: Detected DRAM size: 8192 MB

Check bootmenu

  1. Reset and use NAND to boot into Nvme

2 Likes

Thank you very much for help in testing and writing tutorals.

Small additions: You do not have to copy u-boot bin. Just switch branch. Build.sh does all,see .github/workflows/build.yml

you can use “./build.sh rename” to get the right binaries in source root dir with better name. This is how ci pipeline gets the names and can generate multiple binaries without overwriting.

Thank you for your reply.:smile:
I refer to your version and your contribution.
These modifications give BPI-R4 a lot of flexibility and make it more stable.

Btw i have some helper scripts in builtin env for flashing nand and emmc which are not really documented.

For emmc it is documented for r3 which is basicly same

https://wiki.fw-web.de/doku.php?id=en:bpi-r3:uboot#writing_emmc

They base on useusb which sets variables for further commands (also newboot for booting fit from it).

run useusb

After that you can set filenames for bl2 and fip as vars.

MT7986> setenv bl2file 2023.04/bpi-r3_emmc_bl2.img                                                                                                                                          
MT7986> setenv fipfile 2023.04/bpi-r3_emmc_fip.bin                                                                                                                                          

And then wrote them

MT7986> run wremmc

or

run wrspimnand

So users have no need to know the offsets and complete write command.

thanks for this great thread/instructions! I ended up using pre-build binaries because of a lacking reference to platform_mem_init and bl2_main in the mtk branch (around step 5)

Building mt7988
  LD      /root/mtk-atf-2025/build/mt7988/release/bl2/bl2.elf
/root/aarch64-linux-musl-cross/bin/../lib/gcc/aarch64-linux-musl/11.2.1/../../../../aarch64-linux-musl/bin/ld: /root/mtk-atf-2025/build/mt7988/release/bl2/bl2_el3_entrypoint.o: in function `do_primary_cold_boot':
/root/mtk-atf-2025/bl2/aarch64/bl2_el3_entrypoint.S:29: undefined reference to `platform_mem_init'
/root/aarch64-linux-musl-cross/bin/../lib/gcc/aarch64-linux-musl/11.2.1/../../../../aarch64-linux-musl/bin/ld: /root/mtk-atf-2025/bl2/aarch64/bl2_el3_entrypoint.S:51: undefined reference to `bl2_el3_setup'
/root/aarch64-linux-musl-cross/bin/../lib/gcc/aarch64-linux-musl/11.2.1/../../../../aarch64-linux-musl/bin/ld: /root/mtk-atf-2025/bl2/aarch64/bl2_el3_entrypoint.S:65: undefined reference to `bl2_main'
collect2: error: ld returned 1 exit status
make: *** [Makefile:1595: /root/mtk-atf-2025/build/mt7988/release/bl2/bl2.elf] Error 1
+ ret=2
+ set +x

Any tips on a resolution? The pre-builts for nand don’t seem to have pcie sorted (nvme scan uboot command, but no drive detected). I’ll get it with sdmmc tho :slight_smile:

Did not see this error before…have you added additional options in atf? Maybe you need to run make clean or similar when you have used older atf source

Why not using my build.conf/sh? It does all necessary steps for your…also packing uboot.bin with compression into bl3

Uboot should be same for all my prebuilt binaries for each device,only different dts is different,so possibly i have forgotten to add the pci nodes to emmc dts which is used for nand/nor/emmc.

Which uboot version have you tried (branch)?

hey Frank thanks for the speedy response!

I’m no expert on it, but I am using your build.conf/build.sh I *think. The shown error is only in the mtk-atf-2025 branch. I was able to build on your default git for BPi R4. Are you saying I should use your build.sh from the main branch inside the mtk branch?

I’m also building x86 emulated from M2 chip on UTM Alpine so it’s musl-c, but none of that seems to be the origin of the issue.

No additional options- basically following the shown steps :slight_smile:

Thanks again

After building u-boot switch to mtk-atf-2025 branch and set board again in build.conf and run build.sh.

It should work like for uboot (but without importconfig).

See pipeline in uboot branch for details (device=sdmmc is optional as default)

But yes,if you use different toolchain you could run into issues

Which uboot do you use where pcie does not work on nand?

Seems after moving to dedicated dts for r4 (and r4pro) i inherited an issue from previous dts :slight_smile:

I fixed old dts here,but not the new one

edit pushed a fix for it

1 Like

Hey Frank, OK well I have the mtk branch issue persisting on *both M2 and emulated x86… actually, different errors :sweat_smile:

yes I was on the correct branch for mtk

root@f3e31127d990:/data/mtk-atf-2025# git checkout mtk-atf-2025
Already on 'mtk-atf-2025'
Your branch is up to date with 'origin/mtk-atf-2025'.

so then here’s the error on Mac silicon build (in Docker)

+ make PLAT=mt7988 BOOT_DEVICE=sdmmc DRAM_USE_COMB=1 BL33=u-boot.bin.xz DDR4_4BG_MODE=1 USE_MKIMAGE=1 MKIMAGE=./tools/mkimage BUILD_STRING=b03fcfbc228-bpi-r4-sdmmc all fip
Building mt7988
  CC      common/tf_crc32.c
  CC      drivers/mmc/mmc.c
  CC      drivers/partition/gpt.c
  CC      drivers/partition/partition.c
  CC      plat/mediatek/apsoc_common/bl2/bl2_boot_mmc.c
  CC      plat/mediatek/apsoc_common/bl2/bl2_plat_setup.c
  CC      plat/mediatek/apsoc_common/drivers/mmc/mtk-sd.c
  CC      plat/mediatek/mt7988/bl2/bl2_dev_mmc.c
  CPP     fdts/mt7988.dts
  DTC     /data/mtk-atf-2025/build/mt7988/release/fdts/mt7988.pre.dts
  AS      plat/mediatek/mt7988/bl2/dtb.S
  LD      /data/mtk-atf-2025/build/mt7988/release/bl2/bl2.elf
  BIN     /data/mtk-atf-2025/build/mt7988/release/bl2.bin

Built /data/mtk-atf-2025/build/mt7988/release/bl2.bin successfully

rosetta error: failed to open elf at /lib64/ld-linux-x86-64.so.2
 Trace/breakpoint trap
make: *** [plat/mediatek/apsoc_common/bl2/bl2_image_post.mk:78: /data/mtk-atf-2025/build/mt7988/release/bl2.img] Error 133
+ ret=2
+ set +x

And, I assure you it’s similar for device=spi-nand. This is why I switched to emulated x86… just didn’t have a path forward here. Let me get back to you with the situation there… it takes about 25X longer to reach :sweat_smile:

But basically, it was the same error I linked above (platform_mem_init). Your branch builds fine, it’s the MTK branch I wasn’t able to finish

The uboot precompiled I tried which didn’t recognize nvme (no PCIE I think) was from your releases CI-BUILD-2025 … specifically, bpi-r4_spim-nand_8GB_bl2.img and the matching fip.bin

Thanks!

Got it! There was a stale mkimage in the mtk branch somehow… incompatible w Arm.

I used “make tools-only CROSS_COMPILE=aarch64-linux-gnu-” in your uboot branch, copied tools/mkimage to the mtk branch, and it built! Sorry for the hassle, thank you :slight_smile:

That should be fixed in latest build for R4 (pcie3 was disabled in emmc dts wich is used for spi too). I test r4pro with additional change today (needs a gpio-hog too and ssd in the right slot as pcie2 does not work in uboot due to missing xs-phy driver).

edit: nvme works now for r4pro too (2025-10-bpi and 2026-01-bpi branches).

@ycfunet

Do not do this. “build.sh rename” makes a copy leaving u-boot.bin wich is taken from build.sh in mtk-atf* branch an compressed to the xz file before used.

1 Like

yeah it’s all working now, kinda! Struggling w this image size… for the renamed sysupgrade.itb.

Verifying Hash Integrity … crc32+ sha1+ OK Uncompressing Kernel Image to 46000000 Error: inflate() returned -5 Image too large: increase CONFIG_SYS_BOOTM_LEN Must RESET board to recover

I actually tracked down CONFIG_SYS_BOOTM_LEN in ./build.sh config, and moved it up to 8000000, but still no luck

The stale mkimage was the crux for build, and now I need a valid itb… I see OP has 9.4M size and I have 17+M size w sysupdate.itb

17301812 Dec 27 09:44 openwrt-24.10.4-mediatek-filogic-bananapi_bpi-r4-squashfs-sysupgrade.itb

anyway, more of a user config error now, but the build is solid and sees nvme perfectly! Thanks

try loading itb to 0x5000000 when using openwrt itb, but imho you must load the initramfs itb

https://www.fw-web.de/dokuwiki/doku.php?id=en:bpi-r4:start#openwrt

But this should be discussed in own thread,if loadaddr is not working

1 Like

you’re right it is going a bit off-topic, but I figure I’ll include the final working command which did it for me! I did use the recovery.itb

in uEnv-r4.txt

nvme_boot=0:1
nvme_root=/dev/nvme0n1p1
bootnvme=run usenvme; if test "$device" = "nvme"; then setenv loadaddr 0x48000000; setenv kaddr ${loadaddr}; setenv rdaddr 0x50000000; setenv fit /boot/bpi-r4.itb; load nvme 0:1 ${loadaddr} ${fit}; bootm ${loadaddr}; fi

This kept the changes isolated to that one function… and if anyone else copies, your partition layout may be different.

Thanks for the help! :slight_smile:

You can just set loadaddr globally to keep the command small. Does not make much senae to have different loadaddr based on bootdevice.

0x48000000 works for your openwrt recovery itb?

yep, 0x48M is ideal. 50M didn’t work

The reason for the isolated nvme command is that I thought … it would be nice to have an MMC fallback :sweat_smile:

Once I install the router I want all the boot options, not messing with dip switches (which might be next to a wall) :slight_smile:

As long as you do not use initrd and your kernel does not cross the 0x50000000 boundary (initrd addr) it will work.

1 Like