Skip to content
Open
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
11 changes: 11 additions & 0 deletions debian-rk3576-img.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ actions:
mkdir -p "$IMAGEMNTDIR/var/log/journal"
chattr +C "$IMAGEMNTDIR/var/log/journal"

# U-Boot can't read inline-zstd .dtbo overlays, so the DT drop-in dir must be
# compression=none for later drop-ins to inherit it. Set before @Minimal_stock so
# every profile snapshot carries it via COW.
- action: run
description: Ship /etc/kernel/dtbo uncompressed for U-Boot overlay reads
chroot: false
command: |
set -eu
mkdir -p "$IMAGEMNTDIR/etc/kernel/dtbo"
btrfs property set "$IMAGEMNTDIR/etc/kernel/dtbo" compression none

- action: overlay
description: Copy Linux kernel packages into the target
source: {{ $kerneldir }}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/bin/sh
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# Store the device-tree files U-Boot reads UNCOMPRESSED. U-Boot's btrfs driver cannot
# decompress *inline* zstd extents (regular extents are fine), so small zstd-compressed
# .dtbo overlays fail to load ("zstd_decompress: failed to decompress: 70"). btrfs stores
# small files inline, so overlays are exactly the affected case.
#
# Fix: set the DT dirs to compression=none (new files inherit it) and rewrite the small,
# inline-prone files (the flipper base .dtb + every .dtbo) so their existing extents are
# uncompressed too. Runs on every kernel-install add (build-time apt install AND any later
# install), so new kernels are covered. Idempotent; no-op off btrfs. Ordered before
# 90-loaderentry.install (which then references these files in devicetree-overlay=).
set -e

[ "$KERNEL_INSTALL_LAYOUT" = bls ] || exit 0
[ "$1" = add ] || exit 0
KERNEL_VERSION="$2"
command -v btrfs >/dev/null 2>&1 || exit 0

# rewrite a file so its data lands uncompressed (inherits the dir's compression=none);
# --reflink=never forces a real copy, not an extent-sharing reflink. Best-effort.
uncompress() {
[ -f "$1" ] || return 0
if cp --reflink=never -p "$1" "$1.tmp" && mv -f "$1.tmp" "$1"; then return 0; fi
rm -f "$1.tmp" 2>/dev/null || :
echo "85-flipper-dtb-uncompress: could not rewrite $1 uncompressed" >&2
}

# kernel-shipped DTBs/overlays (build-kernel-bsp installs them here, board-prefixed)
D="/usr/lib/linux-image-$KERNEL_VERSION/rockchip"
if [ -d "$D" ]; then
btrfs property set "$D" compression none 2>/dev/null || exit 0 # not btrfs -> nothing to do
for f in "$D"/rk3576-flipper-one*.dtb "$D"/*.dtbo; do
uncompress "$f"
done
fi

# user overlay drop-in dir (version-independent): create it uncompressed so a later
# `cp x.dtbo /etc/kernel/dtbo/` inherits compression=none, AND rewrite anything already
# there in case it was dropped in while the dir was still compressible.
install -d -m 755 /etc/kernel/dtbo
btrfs property set /etc/kernel/dtbo compression none 2>/dev/null || :
for f in /etc/kernel/dtbo/*.dtbo; do
uncompress "$f"
done

exit 0
83 changes: 70 additions & 13 deletions overlays/usr/lib/flipper-bls.sh
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,22 @@ overlay_paths() { # $1 = subvol prefix (e.g. /@Desktop); remaining args = overl
printf '%s' "$_paths"
}

# Per-profile user drop-in overlays: every *.dtbo under this root's /etc/kernel/dtbo, applied to
# each of the profile's entries. OVERLAY_USER_DIR is the on-disk scan dir (default CONF_ROOT/dtbo,
# i.e. the booted profile; flipper_write_entry points it at the target root). The in-entry path is
# subvol-prefixed like devicetreedir. Silent no-op when the dir is absent or empty.
OVERLAY_USER_REL=/etc/kernel/dtbo
user_overlay_paths() { # $1 = subvol prefix (e.g. /@Desktop)
_udir="${OVERLAY_USER_DIR:-$CONF_ROOT/dtbo}"
[ -d "$_udir" ] || return 0
_upaths=""
for _uf in "$_udir"/*.dtbo; do
[ -f "$_uf" ] || continue # unmatched glob -> skip
_upaths="$_upaths${_upaths:+ }$1$OVERLAY_USER_REL/${_uf##*/}"
done
printf '%s' "$_upaths"
}

# Set BASE_OPTS from CONF_ROOT/cmdline (root=UUID + policy) + this kernel's console layout.
# FIQ kernel -> console=ttyFIQ0 ; mainline -> console=ttyS0 + ttyS4 + fbcon=map:1.
compute_base_opts() {
Expand Down Expand Up @@ -265,6 +281,15 @@ discover_devicetreedir() {
OVERLAY_DIR="${DEVICETREEDIR_SRC:-$DTB_DIR}/$VENDOR"
}

# devicetree-overlay value for an entry: profile overlays ($2 = dtbos names) + user drop-ins,
# subvol-prefixed from $1 (options). Empty if none. Shared by emit_entry and flipper_rewrite_overlay.
dt_overlay_line() {
_p="$(fdt_prefix "$1")"
_o="$(overlay_paths "$_p" $2 2>/dev/null)" || _o=""
_u="$(user_overlay_paths "$_p")"
printf '%s' "$_o${_o:+${_u:+ }}$_u"
}

# emit_entry SUF EXTRA GATE DTBOS -> write one BLS entry for the current $ENTRY_TOKEN.
# The filename is $ENTRY_TOKEN-$KERNEL_VERSION.conf. The token is <NN>-flipperos-<subvol>, so it
# LEADS with the 3-digit menu band and U-Boot's bls (filename sort, descending) orders entries by
Expand All @@ -281,23 +306,14 @@ emit_entry() {
_opts="$BASE_OPTS"
[ -n "$_extra" ] && _opts="$_opts $_extra"
_fn="$ENTRIES/$ENTRY_TOKEN-$KERNEL_VERSION.conf"

if [ -z "$_dtbos" ]; then
write_entry "$_fn" "$(make_title "$_suf")" "$_opts" ""
return 0
fi

# A leading '!' on the overlay list = required (skip the entry if the overlays are
# absent); otherwise optional (the entry is still written, sans overlay).
# a leading '!' on the list = required: skip the whole entry if those profile overlays are absent
_required=0
case "$_dtbos" in '!'*) _required=1; _dtbos="$(trim "${_dtbos#\!}")" ;; esac

_paths="$(overlay_paths "$(fdt_prefix "$_opts")" $_dtbos)" || _paths=""
if [ -z "$_paths" ] && [ "$_required" = 1 ]; then
log "skip $ENTRY_TOKEN (overlays not found for $KERNEL_VERSION)"
if [ "$_required" = 1 ] && [ -z "$(overlay_paths "$(fdt_prefix "$_opts")" $_dtbos 2>/dev/null)" ]; then
log "skip $ENTRY_TOKEN (required overlays not found for $KERNEL_VERSION)"
return 0
fi
write_entry "$_fn" "$(make_title "$_suf")" "$_opts" "$_paths"
write_entry "$_fn" "$(make_title "$_suf")" "$_opts" "$(dt_overlay_line "$_opts" "$_dtbos")"
}

# flipper_write_entry <subvol> <mounted-path> [<origin-hint>]: write a BLS entry for an EXISTING
Expand Down Expand Up @@ -325,8 +341,49 @@ flipper_write_entry() {
# devicetreedir is the target root's OWN kernel dir (KERNEL_VERSION came from it, so it exists).
DEVICETREEDIR_REL="/usr/lib/linux-image-$KERNEL_VERSION"
OVERLAY_DIR="$DTB_DIR/$VENDOR"
OVERLAY_USER_DIR="$_snap/etc/kernel/dtbo" # scan the TARGET root's drop-ins, not the running one
# pin the root's own entry-token so a later runtime apt install in it lands in the same band
mkdir -p "$_snap/etc/kernel" && printf '%s\n' "$ENTRY_TOKEN" > "$_snap/etc/kernel/entry-token"
emit_entry "$(printf '%s' "$_name" | tr -d '@')" "rootflags=subvol=$_name" "" ""
echo "flipper-bls: wrote entry for $_name (kernel $KERNEL_VERSION, token $ENTRY_TOKEN)"
}

# Rewrite the devicetree-overlay line of the booted profile's BLS entries in place from the current
# profile overlays + /etc/kernel/dtbo drop-ins, so changes take effect next boot without a full
# kernel-install (no re-staging, no initrd rebuild).
# $1 scope: ""=the RUNNING kernel's entry only, "all"=every installed kernel of this profile
# Root only; reboot afterwards.
flipper_rewrite_overlay() {
_scope="${1:-}"
[ "$(id -u)" -eq 0 ] || { echo "flipper-bls: must run as root (use sudo)" >&2; return 1; }
ENTRIES="${KERNEL_INSTALL_BOOT_ROOT:-/boot}/loader/entries"
CONF_ROOT="${KERNEL_INSTALL_CONF_ROOT:-/etc/kernel}"
_sv="$(current_subvol)"
[ -n "$_sv" ] || { echo "flipper-bls: cannot determine current subvol" >&2; return 1; }
_run="$(uname -r)"

_dtbos=""
_pf="${FLIPPER_PROFILES:-$CONF_ROOT/flipper-profiles}"
if [ -f "$_pf" ]; then
while IFS='|' read -r _t _s _e _g _d _r; do
case "$_t" in ''|\#*) continue ;; esac
if [ "$(subvol_of "$(trim "$_e")")" = "$_sv" ]; then _dtbos="$(trim "${_d#\!}")"; break; fi
done <"$_pf"
fi

_n=0
for _f in "$ENTRIES"/*.conf; do
[ -f "$_f" ] || continue
_opts="$(sed -n 's/^options[[:space:]]*//p' "$_f")"
[ "$(subvol_of "$_opts")" = "$_sv" ] || continue
KERNEL_VERSION="$(sed -n 's/^version[[:space:]]*//p' "$_f")"
[ -n "$KERNEL_VERSION" ] || continue
[ "$_scope" = all ] || [ "$KERNEL_VERSION" = "$_run" ] || continue
set_dtb_dirs; OVERLAY_DIR="$DTB_DIR/$VENDOR"
_line="$(dt_overlay_line "$_opts" "$_dtbos")"
sed -i '/^devicetree-overlay[[:space:]]/d' "$_f"
[ -n "$_line" ] && printf 'devicetree-overlay %s\n' "$_line" >>"$_f"
_n=$((_n + 1)); echo "flipper-bls: updated ${_f##*/}"
done
[ "$_n" -gt 0 ] || { echo "flipper-bls: no matching entries for $_sv" >&2; return 1; }
}
85 changes: 85 additions & 0 deletions overlays/usr/local/sbin/add-dtbo
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/bin/sh
# add-dtbo - manage this profile's user device-tree overlays in /etc/kernel/dtbo and apply them to
# the booted profile's BLS boot entry without a full kernel-install. Reboot to load changes. By
# default only the BOOTED kernel's entry is touched; --all-kernels does every installed kernel.
set -eu
DROPDIR=/etc/kernel/dtbo

usage() {
cat <<EOF
Usage: add-dtbo [--all-kernels] FILE.dtbo ... copy each FILE into $DROPDIR, then apply
add-dtbo [--all-kernels] --rm NAME ... remove each named overlay from $DROPDIR, then apply
add-dtbo [--all-kernels] --apply apply the overlays already in $DROPDIR
add-dtbo [--all-kernels] --clear remove all user overlays from $DROPDIR, then apply
add-dtbo --list list the *.dtbo in $DROPDIR
add-dtbo --help show this help
EOF
}

_scope=""; _action=""
while [ $# -gt 0 ]; do
case "$1" in
--all-kernels) _scope=all ;;
--rm) _action=rm ;;
--apply) _action=apply ;;
--clear) _action=clear ;;
--list) _action=list ;;
-h|--help) usage; exit 0 ;;
--) shift; break ;;
-?*) echo "Error: unknown option: $1" >&2; usage >&2; exit 1 ;;
*) break ;;
esac
shift
done

if [ $# -gt 0 ]; then
case "$_action" in
'') _action=apply ;;
apply|rm) ;;
*) echo "Error: files cannot be combined with --$_action" >&2; exit 1 ;;
esac
elif [ "$_action" = rm ]; then
echo "Error: --rm needs at least one NAME" >&2; exit 1
fi
[ -n "$_action" ] || { usage; exit 0; }

if [ "$_action" = list ]; then
echo "User overlays in $DROPDIR:"
if ls "$DROPDIR"/*.dtbo >/dev/null 2>&1; then
for f in "$DROPDIR"/*.dtbo; do echo " ${f##*/}"; done
else
echo " (none)"
fi
exit 0
fi

. /usr/lib/flipper-bls.sh 2>/dev/null || { echo "Error: /usr/lib/flipper-bls.sh not found" >&2; exit 1; }
[ "$(id -u)" -eq 0 ] || { echo "Error: must run as root (use sudo)" >&2; exit 1; }

case "$_action" in
clear)
rm -f "$DROPDIR"/*.dtbo
echo "Removed all user overlays from $DROPDIR"
;;
rm)
for f in "$@"; do
_t="$DROPDIR/${f##*/}"
[ -f "$_t" ] || { echo "Error: no such overlay: ${f##*/}" >&2; exit 1; }
rm -f "$_t"
echo "removed ${f##*/}"
done
;;
apply)
install -d -m 755 "$DROPDIR"
btrfs property set "$DROPDIR" compression none 2>/dev/null || :
for f in "$@"; do
[ -f "$f" ] || { echo "Error: no such file: $f" >&2; exit 1; }
case "$f" in *.dtbo) ;; *) echo "Error: not a .dtbo: $f" >&2; exit 1 ;; esac
cp -f --reflink=never "$f" "$DROPDIR/"
echo "copied ${f##*/} -> $DROPDIR/"
done
;;
esac

flipper_rewrite_overlay "$_scope"
echo "Reboot to load changes."