Skip to content

Commit da075a5

Browse files
committed
armbian-install: add UFS install support
JEDEC LUN-id detection from sysfs, new case 9 (install to UFS) via write_uboot_platform_ufs, and UFS_EXCLUDE filter in check_partitions() so non-UFS install paths never offer UFS partitions as destinations.
1 parent 5541888 commit da075a5

1 file changed

Lines changed: 137 additions & 2 deletions

File tree

packages/bsp/common/usr/bin/armbian-install

Lines changed: 137 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,80 @@ root_partition_device=/dev/$root_partition_device_name
5858
[[ -b /dev/nand ]] && nandcheck=$(ls -d -1 /dev/nand* | grep -w 'nand' | awk '{print $NF}');
5959
emmccheck=$(ls -d -1 /dev/mmcblk* 2>/dev/null | grep -w 'mmcblk[0-9]' | grep -v "$root_partition_device");
6060
diskcheck=$(lsblk -Al | awk -F" " '/ disk / {print $1}' | grep -E '^sd|^nvme|^mmc' | grep -v "$root_partition_device_name" | grep -v boot)
61+
62+
# UFS detection. UFS-capable boards (RK3576 variants, Qualcomm, Tegra, …)
63+
# expose multiple SCSI LUNs on a single UFS controller. The JEDEC UFS spec fixes
64+
# the LUN-id-to-role mapping (sizes are vendor-provisioned and vary 4/16/32 MiB):
65+
# LUN 0 = General LUN (rootfs target)
66+
# LUN 1 = Boot LUN A (BootROM idblock target when bBootLunEn=1, factory default)
67+
# LUN 2 = Boot LUN B (BootROM idblock target when bBootLunEn=2)
68+
# LUN 3..255 = additional General/vendor LUNs (excluded from install targets)
69+
# LUN >= 49152 = Well-known LUNs (REPORT_LUNS, BOOT, RPMB, UFS_DEVICE) — no block device
70+
# Classify by walking sysfs from each scsi_host whose proc_name == "ufshcd".
71+
# Reference: JEDEC UFS spec §13.2 (Boot logical units), Toshiba UFS Ver.2.0 App Note.
72+
is_ufs_block() {
73+
local dev_name="$1" sys_path
74+
[[ -e "/sys/block/${dev_name}/device" ]] || return 1
75+
sys_path=$(readlink -f "/sys/block/${dev_name}/device" 2>/dev/null) || return 1
76+
[[ "$sys_path" == *ufshc* || "$sys_path" == *ufs* ]]
77+
}
78+
79+
UFS_GENERAL_LUN=""
80+
UFS_BOOT_LUN_A=""
81+
UFS_BOOT_LUN_B=""
82+
UFS_AUX_LUNS="" # space-separated block device names to filter out of diskcheck
83+
84+
for _ufs_host in /sys/class/scsi_host/host*; do
85+
[[ -r "${_ufs_host}/proc_name" ]] || continue
86+
[[ "$(cat "${_ufs_host}/proc_name" 2>/dev/null)" == "ufshcd" ]] || continue
87+
_ufs_host_id="${_ufs_host##*/host}"
88+
for _scsi_dev in /sys/class/scsi_device/${_ufs_host_id}:*; do
89+
[[ -d "$_scsi_dev" ]] || continue
90+
# Path format: /sys/class/scsi_device/H:C:T:L — last colon-separated field is LUN id.
91+
_lun=${_scsi_dev##*:}
92+
# Skip well-known LUNs (id >= 0xC000) — they expose no /sys/block entry anyway.
93+
[[ "$_lun" =~ ^[0-9]+$ ]] || continue
94+
(( _lun >= 49152 )) && continue
95+
_blk=$(ls "$_scsi_dev/device/block" 2>/dev/null | head -1)
96+
[[ -n "$_blk" ]] || continue
97+
_sz=$(cat "/sys/block/$_blk/size" 2>/dev/null)
98+
[[ -n "$_sz" ]] || continue
99+
_bytes=$(( _sz * 512 ))
100+
case "$_lun" in
101+
0)
102+
# General LUN. Require >=1 GiB to filter out unprovisioned/empty devices.
103+
if (( _bytes >= 1*1024*1024*1024 )); then
104+
UFS_GENERAL_LUN="/dev/$_blk"
105+
fi
106+
;;
107+
1)
108+
UFS_BOOT_LUN_A="/dev/$_blk"
109+
UFS_AUX_LUNS="${UFS_AUX_LUNS} $_blk"
110+
;;
111+
2)
112+
UFS_BOOT_LUN_B="/dev/$_blk"
113+
UFS_AUX_LUNS="${UFS_AUX_LUNS} $_blk"
114+
;;
115+
*)
116+
# LUN 3..255: vendor-extra (e.g. Biwin BWU3AKC46C256G LUN 3 = 8 MiB
117+
# config area). Not a valid install target, hide from diskcheck.
118+
UFS_AUX_LUNS="${UFS_AUX_LUNS} $_blk"
119+
;;
120+
esac
121+
done
122+
done
123+
124+
# Strip Boot LUN A/B + RPMB from diskcheck (they aren't valid install targets).
125+
if [[ -n "$UFS_AUX_LUNS" ]]; then
126+
for _aux in $UFS_AUX_LUNS; do
127+
diskcheck=$(echo "$diskcheck" | grep -vw "$_aux" || true)
128+
done
129+
fi
130+
# Strip the UFS General LUN — only valid via case 9, which sets diskcheck explicitly.
131+
if [[ -n "$UFS_GENERAL_LUN" ]]; then
132+
diskcheck=$(echo "$diskcheck" | grep -vw "${UFS_GENERAL_LUN##*/}" || true)
133+
fi
134+
unset _ufs_host _ufs_host_id _scsi_dev _blk _sz _bytes _aux
61135
# Start mtdcheck with probable MTD block device partitions:
62136
mtdcheck=$(grep 'mtdblock' /proc/partitions | awk '{print $NF}' | xargs)
63137
# Append mtdcheck with probable MTD char devices filtered for partition name(s)
@@ -707,7 +781,15 @@ check_partitions()
707781
IFS=" "
708782
[[ -n "$1" ]] && EXCLUDE=" | grep -v $1"
709783
[[ -n "$2" ]] && INCLUDE=" | grep $2" && diskcheck=$2
710-
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}'"
784+
# Hide UFS partitions from the picker when no explicit disk was requested.
785+
local UFS_EXCLUDE=""
786+
if [[ -z "$2" ]]; then
787+
[[ -n "$UFS_GENERAL_LUN" ]] && UFS_EXCLUDE="${UFS_EXCLUDE} | grep -vE '^${UFS_GENERAL_LUN##*/}'"
788+
for _aux in $UFS_AUX_LUNS; do
789+
UFS_EXCLUDE="${UFS_EXCLUDE} | grep -vE '^${_aux}'"
790+
done
791+
fi
792+
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}'"
711793
AvailablePartitions=$(eval $CMD)
712794

713795
FREE_SPACE=$(sfdisk --list-free /dev/$diskcheck | grep G | tail -1 | awk '{print $4}' | sed "s/G//")
@@ -782,7 +864,7 @@ check_partitions()
782864
} | fdisk /dev/$diskcheck &> /dev/null || true
783865
fi
784866
fi
785-
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"
867+
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"
786868
partprobe
787869
AvailablePartitions=$(eval $CMD)
788870
PartitionOptions=($AvailablePartitions)
@@ -927,6 +1009,13 @@ main()
9271009

9281010
[[ -n $mtdcheck && \
9291011
$(type -t write_uboot_platform_mtd) == function ]] && options+=(7 'Install/Update the bootloader on MTD Flash')
1012+
1013+
# UFS install: requires General LUN + Boot LUN A on a UFS controller AND
1014+
# board-side write_uboot_platform_ufs (provided by BSP for boards that
1015+
# support UFS boot; absent boards skip the option silently).
1016+
[[ -n "$UFS_GENERAL_LUN" && -n "$UFS_BOOT_LUN_A" && \
1017+
$(type -t write_uboot_platform_ufs) == function ]] && \
1018+
options+=(9 "Boot from UFS - system on UFS (${UFS_GENERAL_LUN##*/} + idblock on ${UFS_BOOT_LUN_A##*/})")
9301019
fi
9311020

9321021
# show flasher if there are available block devices
@@ -1024,6 +1113,52 @@ main()
10241113
armbian-config --cmd FLASH1
10251114
return
10261115
;;
1116+
9)
1117+
# Boot from UFS — system on UFS.
1118+
# General LUN holds rootfs + u-boot.itb; Boot LUN A holds idblock.
1119+
title='UFS boot | UFS root install'
1120+
command='Power off'
1121+
_ufs_disk_name="${UFS_GENERAL_LUN##*/}"
1122+
diskcheck="$_ufs_disk_name"
1123+
DISK_ROOT_PART="${UFS_GENERAL_LUN}1"
1124+
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?"
1125+
# /boot must be copied to the UFS rootfs (no separate boot partition):
1126+
# remove '/boot' from rsync exclude list (same as option 4 for MTD).
1127+
sed -i '/boot/d' $EX_LIST
1128+
umount_device "${UFS_GENERAL_LUN}"
1129+
wipefs -aq "${UFS_BOOT_LUN_A}" 2>/dev/null || true
1130+
# Scope the partition picker to the UFS General LUN only.
1131+
check_partitions "$root_partition_device_name" "$_ufs_disk_name"
1132+
format_disk "$DISK_ROOT_PART"
1133+
create_armbian "" "$DISK_ROOT_PART"
1134+
# write_uboot_platform_ufs is provided by the BSP for boards that
1135+
# can boot from UFS; it knows how to lay idblock on Boot LUN A
1136+
# and u-boot.itb on the General LUN.
1137+
write_uboot_platform_ufs "$DIR" "$UFS_BOOT_LUN_A" "$UFS_GENERAL_LUN"
1138+
# create_armbian's "SD boot, SATA root" branch (line ~442) updates
1139+
# /boot/armbianEnv.txt on the *running SD* and writes /media/mmcboot
1140+
# bind-mount stanzas into the target /etc/fstab — both wrong for
1141+
# UFS-only boot (no SD at runtime). Remount the new UFS rootfs and
1142+
# point armbianEnv.txt at the freshly-mkfs'd partition's UUID +
1143+
# scrub the SD-bind stanzas from fstab.
1144+
_ufs_fix_mp=$(mktemp -d /mnt/ufs-fix.XXXXXX)
1145+
if mount -t ext4 "$DISK_ROOT_PART" "$_ufs_fix_mp"; then
1146+
_new_uuid="UUID=$(blkid -s UUID -o value "$DISK_ROOT_PART")"
1147+
if [[ -n "${_new_uuid#UUID=}" && -f "$_ufs_fix_mp/boot/armbianEnv.txt" ]]; then
1148+
sed -e "s|^rootdev=.*|rootdev=${_new_uuid}|" -i "$_ufs_fix_mp/boot/armbianEnv.txt"
1149+
grep -q '^rootdev' "$_ufs_fix_mp/boot/armbianEnv.txt" || echo "rootdev=${_new_uuid}" >> "$_ufs_fix_mp/boot/armbianEnv.txt"
1150+
sed -e "s|^rootfstype=.*|rootfstype=${FilesystemChoosen}|" -i "$_ufs_fix_mp/boot/armbianEnv.txt"
1151+
grep -q '^rootfstype' "$_ufs_fix_mp/boot/armbianEnv.txt" || echo "rootfstype=${FilesystemChoosen}" >> "$_ufs_fix_mp/boot/armbianEnv.txt"
1152+
fi
1153+
if [[ -f "$_ufs_fix_mp/etc/fstab" ]]; then
1154+
sed -i -e '\|/media/mmcboot|d' -e '\|/media/mmcroot|d' "$_ufs_fix_mp/etc/fstab"
1155+
fi
1156+
sync
1157+
umount "$_ufs_fix_mp"
1158+
fi
1159+
rmdir "$_ufs_fix_mp" 2>/dev/null
1160+
sync
1161+
;;
10271162

10281163
*)
10291164
title='UEFI install to internal drive'

0 commit comments

Comments
 (0)