@@ -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}' ) ;
5959emmccheck=$( ls -d -1 /dev/mmcblk* 2> /dev/null | grep -w ' mmcblk[0-9]' | grep -v " $root_partition_device " ) ;
6060diskcheck=$( 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:
62136mtdcheck=$( 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 -a q " ${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 " $FilesystemChoosen " " $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