99# Keys written: standard BLS (title, version, machine-id, sort-key, options, linux,
1010# devicetree, devicetree-overlay, initrd) plus 'devicetreedir' - a U-Boot extension
1111# (DTB directory for cross-board fdtdir auto-selection; a U-Boot alias of 'fdtdir').
12+ #
13+ # The shared entry-writing helpers live in /usr/lib/flipper-bls.sh (also sourced by the
14+ # btrfs snapshot tooling: create_profile / rename_profile).
1215
1316set -e
17+ . /usr/lib/flipper-bls.sh
1418
19+ # ===========================================================================
20+ # kernel-install plugin body.
21+ # ===========================================================================
1522COMMAND=" ${1:? } "
1623KERNEL_VERSION=" ${2:? } "
1724ENTRY_DIR_ABS=" ${3:? } "
@@ -25,113 +32,40 @@ ENTRY_TOKEN="${KERNEL_INSTALL_ENTRY_TOKEN:?}"
2532MACHINE_ID=" ${KERNEL_INSTALL_MACHINE_ID:- } "
2633CONF_ROOT=" ${KERNEL_INSTALL_CONF_ROOT:-/ etc/ kernel} "
2734ENTRIES=" $BOOT_ROOT /loader/entries"
28- ENTRY_STEM=" $ENTRIES /$ENTRY_TOKEN -$KERNEL_VERSION "
2935PROFILES=" ${FLIPPER_PROFILES:-/ etc/ kernel/ flipper-profiles} "
3036KCONFIG=" $BOOT_ROOT /config-$KERNEL_VERSION "
31- VENDOR=rockchip
32- TITLE_MAX=" ${FLIPPER_TITLE_MAX:- 26} "
33-
34- # Where the kernel package installs DTBs/overlays: primary dir + the modules fallback.
35- DTB_DIR=" /usr/lib/linux-image-$KERNEL_VERSION "
36- DTB_DIRS=" $DTB_DIR /usr/lib/modules/$KERNEL_VERSION /dtb"
37-
38- # U-Boot reads the btrfs TOP LEVEL (subvolid 5), so in-entry devicetreedir/overlay paths must
39- # be prefixed with the root subvolume the entry boots -- /@<subvol>/usr/lib/... -- where that
40- # root's DTBs live (the kernel package installs them in every root's /usr/lib). The prefix is
41- # derived per entry from its own rootflags=subvol= (each profile boots a different root). The
42- # staged kernel/initrd live under /boot (the boot subvolume) and are never prefixed.
43- # Set FDT_SUBVOL_PREFIX to override the derivation (e.g. "" if the root is not a subvolume).
44- fdt_prefix () { # $1 = options string -> "/<rootflags-subvol>" (or $FDT_SUBVOL_PREFIX if set)
45- if [ " ${FDT_SUBVOL_PREFIX+x} " = x ]; then printf ' %s' " $FDT_SUBVOL_PREFIX " ; return 0; fi
46- _sv=$( printf ' %s' " $1 " | tr ' ' ' \n' | sed -n ' s/^rootflags=subvol=//p' | tail -n1)
47- [ -n " $_sv " ] && printf ' /%s' " $_sv "
48- return 0
49- }
50-
51- log () { [ " ${KERNEL_INSTALL_VERBOSE:- 0} " -gt 0 ] && echo " flipper-bls: $* " >&2 ; return 0; }
52- die () { echo " flipper-bls: $* " >&2 ; exit 1; }
53- trim () { printf ' %s' " $1 " | sed ' s/^ *//; s/ *$//' ; }
54- # copy a file into the entry dir as root:root 0644
55- stage_file () { install -m 0644 -o root -g root " $1 " " $2 " || die " could not copy '$1 '" ; }
37+ set_dtb_dirs
38+ [ -n " ${BOOT_MNT:- } " ] || BOOT_MNT=" /"
5639
57- # remove: drop the base entry and all profile variants for this kernel
40+ # remove: drop every entry for THIS version that boots the CURRENT root subvol, whatever its
41+ # entry-token (factory 'debian-*' and per-root 'flipperos-*' alike). Keyed on each entry's own
42+ # content (version + rootflags=subvol=), so it never touches another profile's entries and
43+ # survives any purge/cleanup path. kernel-install itself removes this root's staged dir.
5844if [ " $COMMAND " = " remove" ]; then
59- rm -f " $ENTRY_STEM .conf" " $ENTRY_STEM -" * .conf
60- _id=" " && [ -r /etc/os-release ] && _id=" $( . /etc/os-release; printf ' %s' " ${IMAGE_ID:- $ID } " ) " || :
61- if [ -n " $_id " ] && [ " $_id " != " $ENTRY_TOKEN " ]; then
62- _alt=" $ENTRIES /$_id -$KERNEL_VERSION "
63- rm -f " $_alt .conf" " $_alt -" * .conf
64- fi
45+ remove_entries " $( current_subvol) " " $KERNEL_VERSION "
46+ # kernel-install removed this version's staged dir; reap the now-empty per-token parent
47+ # ($BOOT_ROOT/$ENTRY_TOKEN) too. rmdir (not rm -rf) only succeeds if truly empty, so it
48+ # never touches a token dir that still has another version staged.
49+ rmdir " $BOOT_ROOT /$ENTRY_TOKEN " 2> /dev/null || :
6550 exit 0
6651fi
6752[ " $COMMAND " = " add" ] || exit 0
6853
69- # In-entry paths are relative to the partition U-Boot actually reads. On Flipper One
70- # that is always the root partition — /boot is a directory/subvolume on it, never a
71- # separate boot partition U-Boot mounts — so paths must keep the /boot prefix, i.e.
72- # BOOT_MNT=/. (Override BOOT_MNT in the environment if /boot ever becomes its own
73- # partition, which would make boot_rel strip the mountpoint again.)
74- [ -n " $BOOT_MNT " ] || BOOT_MNT=" /"
75-
76- # absolute path under BOOT_ROOT -> path as it must appear inside a BLS entry (relative
77- # to the boot partition; identical to the absolute path when /boot is on the rootfs).
78- boot_rel () { if [ " $BOOT_MNT " = " /" ]; then printf ' %s\n' " $1 " ; else printf ' %s\n' " ${1# " $BOOT_MNT " } " ; fi ; }
79-
8054ENTRY_DIR=" $( boot_rel " $ENTRY_DIR_ABS " ) "
8155[ -d " $ENTRY_DIR_ABS " ] || die " entry directory '$ENTRY_DIR_ABS ' does not exist"
82-
83- # The loader entries dir may not exist yet on a fresh boot partition.
8456mkdir -p " $ENTRIES " || die " could not create '$ENTRIES '"
8557
86- has_config () { [ -f " $KCONFIG " ] && grep -q " ^CONFIG_$1 =[ym]" " $KCONFIG " ; }
58+ # Base cmdline shared by every entry (profiles append their rootflags=subvol=).
59+ compute_base_opts
8760
88- # ===========================================================================
89- # Stage the artifacts shared by every entry (kernel, initrd(s), devicetree[dir]).
90- # ===========================================================================
91-
92- # Base kernel command line (profiles append to this).
93- if [ -f " $CONF_ROOT /cmdline" ]; then
94- BASE_OPTS=" $( grep -Gv ' ^\s*#' " $CONF_ROOT /cmdline" | tr -s ' [:space:]' ' ' ) "
95- elif [ -f /usr/lib/kernel/cmdline ]; then
96- BASE_OPTS=" $( grep -Gv ' ^\s*#' /usr/lib/kernel/cmdline | tr -s ' [:space:]' ' ' ) "
97- else
98- BASE_OPTS=" "
99- fi
100- BASE_OPTS=" $( trim " $BASE_OPTS " ) "
101-
102- # If the boot entries are named after the machine ID, pin systemd.machine_id= on the
103- # kernel command line so the machine ID stays stable inside the initrd (where it isn't
104- # directly accessible yet) and across factory reset on a volatile root.
105- # (No-op unless entry-token == machine-id, e.g. machine-id-based entry naming.)
106- if [ -n " $MACHINE_ID " ] && [ " $ENTRY_TOKEN " = " $MACHINE_ID " ] && ! echo " $BASE_OPTS " | grep -q " systemd.machine_id=" ; then
107- BASE_OPTS=" $BASE_OPTS systemd.machine_id=$MACHINE_ID "
108- fi
109-
110- # Console selection replicating the old features.d/default logic:
111- # FIQ kernel -> console=ttyFIQ0,1500000n8
112- # mainline -> console=ttyS0,1500000n8 console=ttyS4,1500000n8 fbcon=map:1
113- if has_config FIQ_DEBUGGER_CONSOLE; then
114- BASE_OPTS=" $( echo " $BASE_OPTS " | sed ' s/ console=ttyS0,1500000n8 / /g' ) "
115- BASE_OPTS=" $( trim " $BASE_OPTS " ) console=ttyFIQ0,1500000n8"
116- else
117- BASE_OPTS=" $( echo " $BASE_OPTS " | sed ' s/ console=ttyS0,1500000n8 / /g' ) "
118- BASE_OPTS=" $( trim " $BASE_OPTS " ) console=ttyS0,1500000n8"
119- BASE_OPTS=" $( echo " $BASE_OPTS " | sed ' s/ console=ttyS4,1500000n8 / /g' ) "
120- BASE_OPTS=" $( trim " $BASE_OPTS " ) console=ttyS4,1500000n8"
121- BASE_OPTS=" $( echo " $BASE_OPTS " | sed ' s/ console=ttyFIQ0,1500000n8 / /g' ) "
122- BASE_OPTS=" $( trim " $BASE_OPTS " ) fbcon=map:1"
123- fi
124-
125- # Standard BLS sort-key (os-release IMAGE_ID, else ID): a spec-compliant reader uses
126- # it to order entries
127- SORT_KEY=" "
128- [ -r /etc/os-release ] && SORT_KEY=" $( . /etc/os-release; printf ' %s' " ${IMAGE_ID:- $ID } " ) " || :
61+ # Standard BLS sort-key (os-release IMAGE_ID, else ID).
62+ SORT_KEY=" $( os_sort_key) "
12963
13064# Kernel image.
13165stage_file " $KERNEL_IMAGE " " $ENTRY_DIR_ABS /linux"
13266KERNEL_ENTRY=" $ENTRY_DIR /linux"
13367
134- # devicetree (single, optional) — copied into the entry dir.
68+ # devicetree (single, optional): copied into the entry dir.
13569DEVICETREE=" " ; DEVICETREE_ENTRY=" "
13670[ -f " $CONF_ROOT /devicetree" ] && read -r DEVICETREE < " $CONF_ROOT /devicetree" || :
13771if [ -n " $DEVICETREE " ]; then
@@ -144,33 +78,10 @@ if [ -n "$DEVICETREE" ]; then
14478 [ -n " $DEVICETREE_ENTRY " ] || die " device tree blob '$DEVICETREE ' not found"
14579fi
14680
147- # devicetreedir (U-Boot fdtdir extension) — referenced in place, opt-in via a
148- # /etc/kernel/devicetreedir file containing a true boolean. Only when no single
149- # devicetree was configured.
150- DEVICETREEDIR_SRC=" " ; DEVICETREEDIR_REL=" "
151- if [ -z " $DEVICETREE " ] && [ -f " $CONF_ROOT /devicetreedir" ]; then
152- _v=" " ; read -r _v < " $CONF_ROOT /devicetreedir" || :
153- case " $_v " in
154- 1|[Yy]|[Yy][Ee][Ss]|[Tt]|[Tt][Rr][Uu][Ee]|[Oo][Nn])
155- for p in $DTB_DIRS ; do
156- [ -d " $p " ] || continue
157- for d in " $p " /* .dtb " $p " /* /* .dtb " $p " /* /* /* .dtb; do
158- [ -f " $d " ] && { DEVICETREEDIR_SRC=" $p " ; break ; }
159- done
160- [ -n " $DEVICETREEDIR_SRC " ] && break
161- done
162- [ -n " $DEVICETREEDIR_SRC " ] || die " no device-tree directory for $KERNEL_VERSION "
163- DEVICETREEDIR_REL=" $( boot_rel " $DEVICETREEDIR_SRC " ) " # /usr/lib/...; prefixed per entry
164- ;;
165- esac
166- fi
167- # Overlay directory: DT overlays sit flat next to the .dtb files (upstream layout),
168- # i.e. in the same vendor dir the DTBs live in. Path == entry path while /boot is on
169- # the rootfs.
170- OVERLAY_DIR=" ${DEVICETREEDIR_SRC:- $DTB_DIR } /$VENDOR "
81+ # devicetreedir (U-Boot fdtdir extension) + overlay dir.
82+ discover_devicetreedir
17183
172- # initrd(s): from the kernel-install staging area, positional args, or the
173- # distro's well-known names. Collect the in-entry paths once for reuse.
84+ # initrd(s): from the kernel-install staging area, positional args, or well-known names.
17485shift " $INITRD_SHIFT "
17586INITRD_ENTRIES=" "
17687add_initrd () { INITRD_ENTRIES=" $INITRD_ENTRIES ${INITRD_ENTRIES: + } $1 " ; }
@@ -185,104 +96,29 @@ for i in "$KERNEL_INSTALL_STAGING_AREA"/microcode* "$@" "$KERNEL_INSTALL_STAGING
18596 stage_file " $i " " $ENTRY_DIR_ABS /$_bn "
18697 add_initrd " $ENTRY_DIR /$_bn "
18798done
188- # Fallbacks when no initrd was staged into kernel-install (e.g. initramfs-tools).
18999[ -z " $INITRD_ENTRIES " ] && [ -f " $ENTRY_DIR_ABS /initrd" ] && add_initrd " $ENTRY_DIR /initrd"
190100[ -z " $INITRD_ENTRIES " ] && [ -f " $BOOT_ROOT /initramfs-$KERNEL_VERSION .img" ] && add_initrd " $( boot_rel " $BOOT_ROOT /initramfs-$KERNEL_VERSION .img" ) "
191101[ -z " $INITRD_ENTRIES " ] && [ -f " $BOOT_ROOT /initrd.img-$KERNEL_VERSION " ] && add_initrd " $( boot_rel " $BOOT_ROOT /initrd.img-$KERNEL_VERSION " ) "
192102
193- # ===========================================================================
194- # Entry writer + overlay discovery.
195- # ===========================================================================
196-
197- # write_entry FILE TITLE OPTIONS FDTOVERLAYS
198- write_entry () {
199- _f=" $1 " ; _title=" $2 " ; _opts=" $3 " ; _fdtov=" $4 "
200- # devicetreedir is prefixed with THIS entry's root subvol (U-Boot reads subvolid 5)
201- _dtdir=" " ; [ -n " $DEVICETREEDIR_REL " ] && _dtdir=" $( fdt_prefix " $_opts " ) $DEVICETREEDIR_REL "
202- {
203- echo " # Boot Loader Specification type#1 entry (Flipper One)"
204- echo " title $_title "
205- echo " version $KERNEL_VERSION "
206- [ -n " $MACHINE_ID " ] && [ " $ENTRY_TOKEN " = " $MACHINE_ID " ] && echo " machine-id $MACHINE_ID "
207- [ -n " $SORT_KEY " ] && echo " sort-key $SORT_KEY "
208- [ -n " $_opts " ] && echo " options $_opts "
209- echo " linux $KERNEL_ENTRY "
210- [ -n " $DEVICETREE_ENTRY " ] && echo " devicetree $DEVICETREE_ENTRY "
211- [ -n " $_dtdir " ] && echo " devicetreedir $_dtdir "
212- [ -n " $_fdtov " ] && echo " devicetree-overlay $_fdtov "
213- for _i in $INITRD_ENTRIES ; do echo " initrd $_i " ; done
214- } > " $_f "
215- return 0 # don't let a trailing empty conditional's status abort `set -e`
216- }
217-
218- # Echo the space-separated paths of the named DT overlays (flat, next to the DTBs).
219- # Returns non-zero with no output if any one is missing.
220- overlay_paths () { # $1 = subvol prefix (e.g. /@Desktop); remaining args = overlay names
221- _pfx=" $1 " ; shift
222- _paths=" "
223- for _n in " $@ " ; do
224- [ -f " $OVERLAY_DIR /$_n .dtbo" ] || return 1 # check the real on-disk path
225- _paths=" $_paths ${_paths: + } $_pfx$OVERLAY_DIR /$_n .dtbo"
226- done
227- printf ' %s' " $_paths "
228- }
229-
230- # ===========================================================================
231- # Write the entries.
232- # ===========================================================================
233-
234- # Menu titles must fit the device's small screen. A title is "<version> <profile>";
235- # cap the TOTAL length at TITLE_MAX chars by trimming the (long) version while keeping
236- # the profile suffix intact. Entry filenames and the 'version' field keep the full
237- # kernel version, which kernel-install needs to stay consistent with /lib/modules.
238- make_title () {
239- _suf=" $1 "
240- _avail=$(( TITLE_MAX - ${# _suf} - 1 ))
241- if [ " $_avail " -ge 1 ]; then
242- printf ' %s %s' " $( printf ' %s' " $KERNEL_VERSION " | cut -c" 1-$_avail " ) " " $_suf "
243- else
244- printf ' %s' " $_suf " | cut -c" 1-$TITLE_MAX "
245- fi
246- }
247-
248- # Base entry == the [DESKTOP] profile.
249- write_entry " $ENTRY_STEM .conf" " $( make_title ' [DESKTOP]' ) " " $BASE_OPTS " " "
250-
251- [ -f " $PROFILES " ] || { log " $PROFILES not found, only the [DESKTOP] entry written" ; exit 0; }
252-
253- while IFS=' |' read -r tok suf extra gate dtbos legend; do
254- tok=" $( echo " $tok " | tr -d ' [:space:]' ) "
255- case " $tok " in ' ' |\# * ) continue ;; esac
256-
257- suf=" $( trim " $suf " ) "
258- extra=" $( trim " $extra " ) "
259- gate=" $( trim " $gate " ) "
260- dtbos=" $( trim " $dtbos " ) "
261-
262- # gate may list several config symbols; all must be present.
263- gated_out=0; _failed_g=" "
264- for g in $gate ; do has_config " $g " || { gated_out=1; _failed_g=" $g " ; break ; }; done
265- if [ " $gated_out " = 1 ]; then log " skip $tok (CONFIG_$_failed_g not set for $KERNEL_VERSION )" ; continue ; fi
266-
267- opts=" $BASE_OPTS "
268- [ -n " $extra " ] && opts=" $opts $extra "
269-
270- if [ -z " $dtbos " ]; then
271- write_entry " $ENTRY_STEM -$tok .conf" " $( make_title " $suf " ) " " $opts " " "
272- continue
273- fi
274-
275- # A leading '!' on the overlay list = required (skip if overlays absent, like the
276- # old add_board_dtbo -z); otherwise optional (entry is still written, sans overlay).
277- required=0
278- case " $dtbos " in ' !' * ) required=1; dtbos=" $( trim " ${dtbos# \! } " ) " ;; esac
279-
280- paths=" $( overlay_paths " $( fdt_prefix " $opts " ) " $dtbos ) " || paths=" "
281- if [ -z " $paths " ] && [ " $required " = 1 ]; then
282- log " skip $tok (overlays not found for $KERNEL_VERSION )"
283- continue
284- fi
285- write_entry " $ENTRY_STEM -$tok .conf" " $( make_title " $suf " ) " " $opts " " $paths "
286- done < " $PROFILES "
103+ # Write this root's entry. apt/dpkg only ever touch the booted root, so emit ONLY its entry. The
104+ # entry-token (from kernel-install, baked per root as <NN>-flipperos-<subvol>) already carries the
105+ # menu band and IS the /boot/<token>/ staging dir, so there is no slot math here: match the booted
106+ # root to a profile line for its title/overlays, else treat it as a clone (title = name, own root).
107+ CUR_SUBVOL=" $( current_subvol) "
108+ [ -n " $CUR_SUBVOL " ] || die " cannot determine current root subvol (findmnt)"
109+
110+ matched=0
111+ if [ -f " $PROFILES " ]; then
112+ while IFS=' |' read -r tok suf extra gate dtbos legend; do
113+ tok=" $( echo " $tok " | tr -d ' [:space:]' ) "
114+ case " $tok " in ' ' |\# * ) continue ;; esac
115+ extra=" $( trim " $extra " ) "
116+ if [ " $( subvol_of " $extra " ) " = " $CUR_SUBVOL " ]; then
117+ emit_entry " $( trim " $suf " ) " " $extra " " $( trim " $gate " ) " " $( trim " $dtbos " ) "
118+ matched=1; break
119+ fi
120+ done < " $PROFILES "
121+ fi
122+ [ " $matched " = 1 ] || emit_entry " $( printf ' %s' " $CUR_SUBVOL " | tr -d ' @' ) " " rootflags=subvol=$CUR_SUBVOL " " " " "
287123
288124exit 0
0 commit comments