Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 42 additions & 6 deletions config/boards/nanopi-m5.conf
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
# Rockchip RK3576 SoC octa core 4-16GB RAM SoC 2xGbE UFS USB3 WIFI NvME
# Rockchip RK3576 SoC octa core 4-16GB RAM SoC 2xGbE UFS USB3 WIFI NvMe
BOARD_NAME="NanoPi M5"
BOARD_VENDOR="friendlyelec"
BOARDFAMILY="rk35xx"
BOOT_SOC="rk3576"
BOOTCONFIG="nanopi-m5-rk3576_defconfig"
# WIP: current kernel (no comments on same line as KERNEL_TARGET)
KERNEL_TARGET="vendor,edge"
KERNEL_TARGET="current,edge,vendor"
KERNEL_TEST_TARGET="vendor,current"
FULL_DESKTOP="yes"
ASOUND_STATE="asound.state.nanopi-m5"
BOOT_LOGO="desktop"
BOOT_FDT_FILE="rockchip/rk3576-nanopi-m5.dtb"
BOOT_SCENARIO="spl-blobs"
BOOT_SUPPORT_SPI="yes"
BOOT_SPI_RKSPI_LOADER="yes"
BOOT_SCENARIO="vendor-spl-blobs"
IMAGE_PARTITION_TABLE="gpt"
BOARD_MAINTAINER="SuperKali"
INTRODUCED="2023"
Expand All @@ -25,3 +24,40 @@ function post_family_tweaks__nanopi-m5_naming_audios() {
echo 'SUBSYSTEM=="sound", ENV{ID_PATH}=="platform-rt5616-sound", ENV{SOUND_DESCRIPTION}="RT5616 Audio"' >> $SDCARD/etc/udev/rules.d/90-naming-audios.rules
return 0
}

function post_family_config__nanopi_m5_use_mainline_uboot() {
display_alert "$BOARD" "Mainline U-Boot v2026.04 + vendor SPL hybrid" "info"

declare -g BOOTDELAY=1
declare -g BOOTSOURCE="https://github.com/u-boot/u-boot.git"
declare -g BOOTBRANCH="tag:v2026.04"
declare -g BOOTPATCHDIR="v2026.04"
unset BOOT_FDT_FILE

unset write_uboot_platform write_uboot_platform_mtd

# Bootloader on SD/eMMC.
function write_uboot_platform() {
dd "if=$1/idbloader.img" "of=$2" seek=64 conv=notrunc status=none
dd "if=$1/u-boot.itb" "of=$2" seek=16384 conv=notrunc status=none
}

# Bootloader on SPI NOR (mainline binman image).
function write_uboot_platform_mtd() {
flashcp -v -p "$1/u-boot-rockchip-spi.bin" /dev/mtd0
}

# Bootloader on UFS: idblock on Boot LUN A, u-boot.itb on General LUN.
function write_uboot_platform_ufs() {
dd "if=$1/idbloader.img" "of=$2" bs=4096 seek=8 conv=notrunc,fsync status=none
dd "if=$1/u-boot.itb" "of=$3" bs=4096 seek=2048 conv=notrunc,fsync status=none
}
}

# Boot order: SD → UFS (scsi uclass) → NVMe → net.
function pre_config_uboot_target__nanopi_m5_patch_rockchip_common_boot_order() {
declare -a rockchip_uboot_targets=("mmc0" "scsi" "nvme" "pxe" "dhcp")
display_alert "u-boot for ${BOARD}/${BRANCH}" "boot order '${rockchip_uboot_targets[*]}'" "info"
sed -i -e "s/#define BOOT_TARGETS.*/#define BOOT_TARGETS \"${rockchip_uboot_targets[*]}\"/" include/configs/rockchip-common.h
regular_git diff -u include/configs/rockchip-common.h || true
}
51 changes: 49 additions & 2 deletions config/sources/families/include/rockchip64_common.inc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ esac
# Available options for BOOT_SCENARIO are:
# - only-blobs: proprietary rockchip ddrbin, miniloader and ATF
# - spl-blobs: proprietary rockchip ddrbin and ATF, but uses mainline u-boot SPL in place of rockchip miniloader
# - vendor-spl-blobs: like spl-blobs but FlashBoot in boot_merger ini points at a vendor SPL blob declared via
# VENDOR_SPL_PATH instead of the mainline-built ./spl/u-boot-spl.bin. Used for boards where mainline SPL has
# unresolved SoC-specific issues, e.g. NanoPi M5 (RK3576 mainline SPL has the UFS OCS load_addr bug). Also
# triggers binman to deploy u-boot-rockchip-spi.bin so SPI flash can use mainline SPL (which knows the chip
# via DT) while SD/eMMC/UFS use the vendor SPL idblock.
# - tpl-spl-blob: uses mainline u-boot TPL and SPL with proprietary rockchip ATF blob
# - tpl-blob-atf-mainline: proprietary rockchip ddrbin + mainline u-boot SPL + mainline ATF
# - blobless: mainline u-boot TPL + mainline u-boot SPL + mainline ATF
Expand Down Expand Up @@ -192,6 +197,19 @@ prepare_boot_configuration() {
spl-blobs)
UBOOT_TARGET_MAP="BL31=$RKBIN_DIR/$BL31_BLOB spl/u-boot-spl.bin u-boot.dtb u-boot.itb;;idbloader.img u-boot.itb"
;;
vendor-spl-blobs)
# Like spl-blobs but boot_merger uses VENDOR_SPL_PATH for FlashBoot.
# Empty make-target list (env vars only): binman default goal produces
# u-boot.itb + u-boot-rockchip-spi.bin as side products. boot_merger
# then overwrites idbloader.img with the vendor-SPL version.
UBOOT_TARGET_MAP="BL31=$RKBIN_DIR/$BL31_BLOB ROCKCHIP_TPL=$RKBIN_DIR/$DDR_BLOB;;idbloader.img u-boot.itb u-boot-rockchip-spi.bin"
# Default VENDOR_SPL_PATH per SoC; board can still override.
if [[ -z "${VENDOR_SPL_PATH:-}" ]]; then
case "$BOOT_SOC" in
rk3576) declare -g VENDOR_SPL_PATH="${RKBIN_DIR}/rk35/rk3576_spl_v1.08.bin" ;;
esac
fi
;;
only-blobs)
UBOOT_TARGET_MAP="u-boot-dtb.bin;;idbloader.bin uboot.img trust.bin"
;;
Expand Down Expand Up @@ -226,12 +244,24 @@ uboot_custom_postprocess() {
:
;;

spl-blobs | tpl-blob-atf-mainline)
spl-blobs | tpl-blob-atf-mainline | vendor-spl-blobs)
# Bomb if DDR_BLOB not defined or does not exist
declare SPL_BIN_PATH="${RKBIN_DIR}/${DDR_BLOB}"
[[ -z ${SPL_BIN_PATH} ]] && exit_with_error "DDR_BLOB not defined for scenario ${BOOT_SCENARIO}"
[[ ! -f "${SPL_BIN_PATH}" ]] && exit_with_error "DDR_BLOB ${SPL_BIN_PATH} does not exist for scenario ${BOOT_SCENARIO}"

# vendor-spl-blobs: FlashBoot in boot_merger points at the vendor SPL blob
# declared by the board via VENDOR_SPL_PATH (instead of the mainline-built
# ./spl/u-boot-spl.bin). Everything else (DDR, boost, usbplug, BL31) is
# unchanged from spl-blobs.
declare FLASH_BOOT_BIN="./spl/u-boot-spl.bin"
if [[ "$BOOT_SCENARIO" == "vendor-spl-blobs" ]]; then
[[ -z "${VENDOR_SPL_PATH:-}" ]] && exit_with_error "VENDOR_SPL_PATH must be set for BOOT_SCENARIO=vendor-spl-blobs"
[[ ! -f "${VENDOR_SPL_PATH}" ]] && exit_with_error "VENDOR_SPL_PATH (${VENDOR_SPL_PATH}) does not exist"
FLASH_BOOT_BIN="${VENDOR_SPL_PATH}"
display_alert "boot_merger for '${BOOT_SOC}'" "using vendor SPL ${VENDOR_SPL_PATH##*/}" "info"
fi

if [[ "$BOOT_SOC" == "rk3576" ]]; then
display_alert "boot_merger for '${BOOT_SOC}' for scenario ${BOOT_SCENARIO}" "SPL_BIN_PATH: ${SPL_BIN_PATH}" "debug"
RKBOOT_INI_FILE=rk3576.ini
Expand All @@ -240,7 +270,7 @@ uboot_custom_postprocess() {
sed -i "s|Path1=.*rk3576_ddr.*$|Path1=${SPL_BIN_PATH}|g" $RKBOOT_INI_FILE
sed -i "s|Path1=.*rk3576_usbplug.*$|Path1=${RKBIN_DIR}/rk35/rk3576_usbplug_v1.03.bin|g" $RKBOOT_INI_FILE
sed -i "s|FlashData=.*$|FlashData=${SPL_BIN_PATH}|g" $RKBOOT_INI_FILE
sed -i "s|FlashBoot=.*$|FlashBoot=./spl/u-boot-spl.bin|g" $RKBOOT_INI_FILE
sed -i "s|FlashBoot=.*$|FlashBoot=${FLASH_BOOT_BIN}|g" $RKBOOT_INI_FILE
sed -i "s|IDB_PATH=.*$|IDB_PATH=idbloader.img|g" $RKBOOT_INI_FILE
run_host_x86_binary_logged $RKBIN_DIR/tools/boot_merger $RKBOOT_INI_FILE
rm -f $RKBOOT_INI_FILE
Expand Down Expand Up @@ -310,6 +340,23 @@ write_uboot_platform() {
fi
}

write_uboot_platform_ufs() {
# Args: $1 = u-boot deploy dir (DIR), $2 = Boot LUN A (e.g. /dev/sdb), $3 = General LUN (e.g. /dev/sda).
# Generic mainline-stack writer: idbloader.img → Boot LUN A @ 32 KiB; u-boot.itb → General LUN @ 8 MiB.
# UFS LUNs expose 4 KiB logical blocks, so bs=4096 with pre-divided seek values.
# Boards needing a vendor idblock (e.g. RK3576 NanoPi M5) override this in their post_family_config hook.
local logging_prelude=""
[[ $(type -t run_host_command_logged) == function ]] && logging_prelude="run_host_command_logged"

if [[ -f $1/idbloader.img && -f $1/u-boot.itb ]]; then
${logging_prelude} dd if=$1/idbloader.img of=$2 bs=4096 seek=8 conv=notrunc,fsync status=none
${logging_prelude} dd if=$1/u-boot.itb of=$3 bs=4096 seek=2048 conv=notrunc,fsync status=none
else
echo "write_uboot_platform_ufs: no idbloader.img + u-boot.itb pair in $1; board hook must override."
exit 1
fi
}

write_uboot_platform_mtd() {
FILES=$(find "$1" -maxdepth 1 -type f \( -name "rkspi_loader*.img" -o -name "u-boot-rockchip-spi*.bin" \))
if [ -z "$FILES" ]; then
Expand Down
1 change: 1 addition & 0 deletions lib/functions/compilation/uboot.sh
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ function compile_uboot() {
DIR=/usr/lib/$uboot_name
$(declare -f write_uboot_platform || true)
$(declare -f write_uboot_platform_mtd || true)
$(declare -f write_uboot_platform_ufs || true)
$(declare -f setup_write_uboot_platform || true)
EOF

Expand Down
139 changes: 137 additions & 2 deletions packages/bsp/common/usr/bin/armbian-install
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,80 @@ root_partition_device=/dev/$root_partition_device_name
[[ -b /dev/nand ]] && nandcheck=$(ls -d -1 /dev/nand* | grep -w 'nand' | awk '{print $NF}');
emmccheck=$(ls -d -1 /dev/mmcblk* 2>/dev/null | grep -w 'mmcblk[0-9]' | grep -v "$root_partition_device");
diskcheck=$(lsblk -Al | awk -F" " '/ disk / {print $1}' | grep -E '^sd|^nvme|^mmc' | grep -v "$root_partition_device_name" | grep -v boot)

# UFS detection. UFS-capable boards (RK3576 variants, Qualcomm, Tegra, …)
# expose multiple SCSI LUNs on a single UFS controller. The JEDEC UFS spec fixes
# the LUN-id-to-role mapping (sizes are vendor-provisioned and vary 4/16/32 MiB):
# LUN 0 = General LUN (rootfs target)
# LUN 1 = Boot LUN A (BootROM idblock target when bBootLunEn=1, factory default)
# LUN 2 = Boot LUN B (BootROM idblock target when bBootLunEn=2)
# LUN 3..255 = additional General/vendor LUNs (excluded from install targets)
# LUN >= 49152 = Well-known LUNs (REPORT_LUNS, BOOT, RPMB, UFS_DEVICE) — no block device
# Classify by walking sysfs from each scsi_host whose proc_name == "ufshcd".
# Reference: JEDEC UFS spec §13.2 (Boot logical units), Toshiba UFS Ver.2.0 App Note.
is_ufs_block() {
local dev_name="$1" sys_path
[[ -e "/sys/block/${dev_name}/device" ]] || return 1
sys_path=$(readlink -f "/sys/block/${dev_name}/device" 2>/dev/null) || return 1
[[ "$sys_path" == *ufshc* || "$sys_path" == *ufs* ]]
}

UFS_GENERAL_LUN=""
UFS_BOOT_LUN_A=""
UFS_BOOT_LUN_B=""
UFS_AUX_LUNS="" # space-separated block device names to filter out of diskcheck

for _ufs_host in /sys/class/scsi_host/host*; do
[[ -r "${_ufs_host}/proc_name" ]] || continue
[[ "$(cat "${_ufs_host}/proc_name" 2>/dev/null)" == "ufshcd" ]] || continue
_ufs_host_id="${_ufs_host##*/host}"
for _scsi_dev in /sys/class/scsi_device/${_ufs_host_id}:*; do
[[ -d "$_scsi_dev" ]] || continue
# Path format: /sys/class/scsi_device/H:C:T:L — last colon-separated field is LUN id.
_lun=${_scsi_dev##*:}
# Skip well-known LUNs (id >= 0xC000) — they expose no /sys/block entry anyway.
[[ "$_lun" =~ ^[0-9]+$ ]] || continue
(( _lun >= 49152 )) && continue
_blk=$(ls "$_scsi_dev/device/block" 2>/dev/null | head -1)
[[ -n "$_blk" ]] || continue
_sz=$(cat "/sys/block/$_blk/size" 2>/dev/null)
[[ -n "$_sz" ]] || continue
_bytes=$(( _sz * 512 ))
case "$_lun" in
0)
# General LUN. Require >=1 GiB to filter out unprovisioned/empty devices.
if (( _bytes >= 1*1024*1024*1024 )); then
UFS_GENERAL_LUN="/dev/$_blk"
fi
;;
1)
UFS_BOOT_LUN_A="/dev/$_blk"
UFS_AUX_LUNS="${UFS_AUX_LUNS} $_blk"
;;
2)
UFS_BOOT_LUN_B="/dev/$_blk"
UFS_AUX_LUNS="${UFS_AUX_LUNS} $_blk"
;;
*)
# LUN 3..255: vendor-extra (e.g. Biwin BWU3AKC46C256G LUN 3 = 8 MiB
# config area). Not a valid install target, hide from diskcheck.
UFS_AUX_LUNS="${UFS_AUX_LUNS} $_blk"
;;
esac
done
done

# Strip Boot LUN A/B + RPMB from diskcheck (they aren't valid install targets).
if [[ -n "$UFS_AUX_LUNS" ]]; then
for _aux in $UFS_AUX_LUNS; do
diskcheck=$(echo "$diskcheck" | grep -vw "$_aux" || true)
done
fi
# Strip the UFS General LUN — only valid via case 9, which sets diskcheck explicitly.
if [[ -n "$UFS_GENERAL_LUN" ]]; then
diskcheck=$(echo "$diskcheck" | grep -vw "${UFS_GENERAL_LUN##*/}" || true)
fi
unset _ufs_host _ufs_host_id _scsi_dev _blk _sz _bytes _aux
# Start mtdcheck with probable MTD block device partitions:
mtdcheck=$(grep 'mtdblock' /proc/partitions | awk '{print $NF}' | xargs)
# Append mtdcheck with probable MTD char devices filtered for partition name(s)
Expand Down Expand Up @@ -707,7 +781,15 @@ check_partitions()
IFS=" "
[[ -n "$1" ]] && EXCLUDE=" | grep -v $1"
[[ -n "$2" ]] && INCLUDE=" | grep $2" && diskcheck=$2
CMD="lsblk -io KNAME,FSTYPE,SIZE,TYPE,MOUNTPOINT | grep -v -w $root_partition_name $INCLUDE $EXCLUDE | grep -E '^sd|^nvme|^md|^mmc' | awk -F\" \" '/ part | raid..? / {print \$1}'"
# Hide UFS partitions from the picker when no explicit disk was requested.
local UFS_EXCLUDE=""
if [[ -z "$2" ]]; then
[[ -n "$UFS_GENERAL_LUN" ]] && UFS_EXCLUDE="${UFS_EXCLUDE} | grep -vE '^${UFS_GENERAL_LUN##*/}'"
for _aux in $UFS_AUX_LUNS; do
UFS_EXCLUDE="${UFS_EXCLUDE} | grep -vE '^${_aux}'"
done
fi
CMD="lsblk -io KNAME,FSTYPE,SIZE,TYPE,MOUNTPOINT | grep -v -w $root_partition_name $INCLUDE $EXCLUDE $UFS_EXCLUDE | grep -E '^sd|^nvme|^md|^mmc' | awk -F\" \" '/ part | raid..? / {print \$1}'"
AvailablePartitions=$(eval $CMD)

FREE_SPACE=$(sfdisk --list-free /dev/$diskcheck | grep G | tail -1 | awk '{print $4}' | sed "s/G//")
Expand Down Expand Up @@ -782,7 +864,7 @@ check_partitions()
} | fdisk /dev/$diskcheck &> /dev/null || true
fi
fi
CMD="lsblk -io KNAME,FSTYPE,SIZE,TYPE,MOUNTPOINT,PARTTYPENAME | grep -v -w $root_partition_name $INCLUDE $EXCLUDE | grep Linux | grep -E '^sd|^nvme|^md|^mmc' | awk -F\" \" '/ part | raid..? / {print \$1}' | uniq | sed 's|^|/dev/|' | nl | xargs echo -n"
CMD="lsblk -io KNAME,FSTYPE,SIZE,TYPE,MOUNTPOINT,PARTTYPENAME | grep -v -w $root_partition_name $INCLUDE $EXCLUDE $UFS_EXCLUDE | grep Linux | grep -E '^sd|^nvme|^md|^mmc' | awk -F\" \" '/ part | raid..? / {print \$1}' | uniq | sed 's|^|/dev/|' | nl | xargs echo -n"
partprobe
AvailablePartitions=$(eval $CMD)
PartitionOptions=($AvailablePartitions)
Expand Down Expand Up @@ -927,6 +1009,13 @@ main()

[[ -n $mtdcheck && \
$(type -t write_uboot_platform_mtd) == function ]] && options+=(7 'Install/Update the bootloader on MTD Flash')

# UFS install: requires General LUN + Boot LUN A on a UFS controller AND
# board-side write_uboot_platform_ufs (provided by BSP for boards that
# support UFS boot; absent boards skip the option silently).
[[ -n "$UFS_GENERAL_LUN" && -n "$UFS_BOOT_LUN_A" && \
$(type -t write_uboot_platform_ufs) == function ]] && \
options+=(9 "Boot from UFS - system on UFS (${UFS_GENERAL_LUN##*/} + idblock on ${UFS_BOOT_LUN_A##*/})")
fi

# show flasher if there are available block devices
Expand Down Expand Up @@ -1024,6 +1113,52 @@ main()
armbian-config --cmd FLASH1
return
;;
9)
# Boot from UFS — system on UFS.
# General LUN holds rootfs + u-boot.itb; Boot LUN A holds idblock.
title='UFS boot | UFS root install'
command='Power off'
_ufs_disk_name="${UFS_GENERAL_LUN##*/}"
diskcheck="$_ufs_disk_name"
DISK_ROOT_PART="${UFS_GENERAL_LUN}1"
show_warning "This script will erase the UFS storage:\n General LUN: ${UFS_GENERAL_LUN}\n Boot LUN A : ${UFS_BOOT_LUN_A}\n\nContinue?"
# /boot must be copied to the UFS rootfs (no separate boot partition):
# remove '/boot' from rsync exclude list (same as option 4 for MTD).
sed -i '/boot/d' $EX_LIST
umount_device "${UFS_GENERAL_LUN}"
wipefs -aq "${UFS_BOOT_LUN_A}" 2>/dev/null || true
# Scope the partition picker to the UFS General LUN only.
check_partitions "$root_partition_device_name" "$_ufs_disk_name"
format_disk "$DISK_ROOT_PART"
create_armbian "" "$DISK_ROOT_PART"
# write_uboot_platform_ufs is provided by the BSP for boards that
# can boot from UFS; it knows how to lay idblock on Boot LUN A
# and u-boot.itb on the General LUN.
write_uboot_platform_ufs "$DIR" "$UFS_BOOT_LUN_A" "$UFS_GENERAL_LUN"
# create_armbian's "SD boot, SATA root" branch (line ~442) updates
# /boot/armbianEnv.txt on the *running SD* and writes /media/mmcboot
# bind-mount stanzas into the target /etc/fstab — both wrong for
# UFS-only boot (no SD at runtime). Remount the new UFS rootfs and
# point armbianEnv.txt at the freshly-mkfs'd partition's UUID +
# scrub the SD-bind stanzas from fstab.
_ufs_fix_mp=$(mktemp -d /mnt/ufs-fix.XXXXXX)
if mount -t "$FilesystemChoosen" "$DISK_ROOT_PART" "$_ufs_fix_mp"; then
_new_uuid="UUID=$(blkid -s UUID -o value "$DISK_ROOT_PART")"
if [[ -n "${_new_uuid#UUID=}" && -f "$_ufs_fix_mp/boot/armbianEnv.txt" ]]; then
sed -e "s|^rootdev=.*|rootdev=${_new_uuid}|" -i "$_ufs_fix_mp/boot/armbianEnv.txt"
grep -q '^rootdev' "$_ufs_fix_mp/boot/armbianEnv.txt" || echo "rootdev=${_new_uuid}" >> "$_ufs_fix_mp/boot/armbianEnv.txt"
sed -e "s|^rootfstype=.*|rootfstype=${FilesystemChoosen}|" -i "$_ufs_fix_mp/boot/armbianEnv.txt"
grep -q '^rootfstype' "$_ufs_fix_mp/boot/armbianEnv.txt" || echo "rootfstype=${FilesystemChoosen}" >> "$_ufs_fix_mp/boot/armbianEnv.txt"
fi
if [[ -f "$_ufs_fix_mp/etc/fstab" ]]; then
sed -i -e '\|/media/mmcboot|d' -e '\|/media/mmcroot|d' "$_ufs_fix_mp/etc/fstab"
fi
sync
umount "$_ufs_fix_mp"
fi
rmdir "$_ufs_fix_mp" 2>/dev/null
sync
;;

*)
title='UEFI install to internal drive'
Expand Down
Loading
Loading