diff --git a/debian-rk3576-img.yaml b/debian-rk3576-img.yaml index 72ea95d..128a218 100644 --- a/debian-rk3576-img.yaml +++ b/debian-rk3576-img.yaml @@ -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 }} diff --git a/overlays/configs/kernel/install.d/85-flipper-dtb-uncompress.install b/overlays/configs/kernel/install.d/85-flipper-dtb-uncompress.install new file mode 100755 index 0000000..9917b06 --- /dev/null +++ b/overlays/configs/kernel/install.d/85-flipper-dtb-uncompress.install @@ -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 diff --git a/overlays/usr/lib/flipper-bls.sh b/overlays/usr/lib/flipper-bls.sh index 861282c..35d7023 100644 --- a/overlays/usr/lib/flipper-bls.sh +++ b/overlays/usr/lib/flipper-bls.sh @@ -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() { @@ -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 -flipperos-, so it # LEADS with the 3-digit menu band and U-Boot's bls (filename sort, descending) orders entries by @@ -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 []: write a BLS entry for an EXISTING @@ -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; } +} diff --git a/overlays/usr/local/sbin/add-dtbo b/overlays/usr/local/sbin/add-dtbo new file mode 100755 index 0000000..1f3d474 --- /dev/null +++ b/overlays/usr/local/sbin/add-dtbo @@ -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 <&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."