File: //usr/share/flash-kernel/functions
# Copyright (C) 2006 Joey Hess
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2016 Martin Michlmayr <tbm@cyrius.com>
# Copyright (C) 2011 Loïc Minier <lool@dooz.org>
# Copyright (C) 2011 Julian Andres Klode <jak@debian.org>
# Copyright (C) 2013-2016 Ian Campbell <ijc@debian.org>
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
# USA.
BOOTSCRIPTS_DIR="${FK_CHECKOUT:-/etc/flash-kernel}/bootscript"
ITSFILES_DIR="${FK_CHECKOUT:-/usr/share/flash-kernel}/its"
FK_ETC_MACHINE="${FK_ETC_MACHINE:-/etc/flash-kernel/machine}"
FK_DEFAULTS="${FK_DEFAULTS:-/etc/default/flash-kernel}"
PROC_CPUINFO="${FK_PROC_CPUINFO:-/proc/cpuinfo}"
PROC_DTMODEL="${FK_PROC_DTMODEL:-/proc/device-tree/model}"
PROC_MTD="/proc/mtd"
read_machine_db() {
if [ -f "${FK_ETC_DB:-/etc/flash-kernel/db}" ]; then
cat "${FK_ETC_DB:-/etc/flash-kernel/db}"
fi
cat "${FK_CHECKOUT:-$FK_DIR}/db/"*.db
}
MACHINE_DB="$(read_machine_db)"
error() {
echo "$@" >&2
exit 1
}
mtdblock() {
local mtdname="$1"
local dev=`sed -rn "s,^mtd([^:]*).*\"$mtdname\"\$,/dev/mtdblock\\1,p" "$PROC_MTD"`
# The mtdblock() function gets also called by the testsuite during
# the package build; don't run modprobe and udevadm then. Invasive
# actions like loading modules and calling into udev should not
# happen at build time and they are not necessary for the testsuite.
if [ -z "${FK_TESTSUITE_RUNNING}" ]; then
modprobe -q mtdblock && udevadm settle --exit-if-exists=$dev || :
fi
echo $dev
}
mtdchar() {
local mtdname="$1"
local dev=`sed -rn "s,^mtd([^:]*).*\"$mtdname\"\$,/dev/mtd\\1,p" "$PROC_MTD"`
echo $dev
}
check_block_dev() {
local dev="$1"
if [ ! -b "$dev" ]; then
error "$dev is not a block device"
fi
}
check_char_dev() {
local dev="$1"
if [ ! -c "$dev" ]; then
error "$dev is not a character device"
fi
}
mtdsize() {
local mtdname="$1"
size=$(grep "\"$mtdname\"$" "$PROC_MTD" | cut -d " " -f 2)
printf "%d" "0x$size"
}
check_kflavors() {
local kvers="$1"
local kflavor="$2"
if [ "$kflavor" = "any" ]; then
return 0
fi
# count flavor+ as valid
kvers=${kvers%%+}
if [ "${kvers}" != "${kvers%%$kflavor}" ]; then
# kernel version ended with flavor
return 0
fi
return 1
}
check_mtd_size() {
local mtd_name="$1"
local required_size="$2"
local actual_size="$3"
local what="$4"
if [ $required_size -gt $actual_size ]; then
case $what in
initrd)
(
echo
echo "The initial ramdisk is too large. This is often due to the unnecessary inclusion"
echo "of all kernel modules in the image. To fix this set MODULES=dep in one or both"
echo "/etc/initramfs-tools/conf.d/driver-policy (if it exists) and"
echo "/etc/initramfs-tools/initramfs.conf and then run 'update-initramfs -u -k $kvers'"
echo
) >&2
;;
esac
error "Not enough space for $what in MTD '$mtd_name' (need $required_size but is actually $actual_size)."
fi
}
check_supported() {
local machine="$1"
local field
local value
echo "$MACHINE_DB" | {
while read field value; do
if [ "$field" = "Machine:" ] &&
match_machine "$machine" "$value"; then
return 0
fi
done
return 1
}
}
get_root_uuid() {
echo $(blkid -o value -s UUID $(mount | grep "on /${1%/} " | tail -n1 | cut -d' ' -f1))
}
get_cpuinfo_hardware() {
grep "^Hardware" "$PROC_CPUINFO" | sed 's/Hardware\s*:\s*//'
}
get_dt_model() {
cat "$PROC_DTMODEL"
}
get_machine() {
if [ -n "$FK_MACHINE" ]; then
[ "x$FK_MACHINE" = "xnone" ] && exit 0
machine="$FK_MACHINE"
elif [ -f "$FK_ETC_MACHINE" ]; then
machine="$(cat "$FK_ETC_MACHINE")"
elif [ -f "$PROC_DTMODEL" ]; then
machine="$(get_dt_model)"
else
machine="$(get_cpuinfo_hardware)"
fi
}
get_kfile_suffix() {
local kfile="$1"
local tail="${2:+-$2}"
echo "$kfile" | sed -e "s/.*-\([^-]*$tail\)/\\1/"
}
# match a machine name (in $1) to a machine name / pattern ($2) from the
# database; patterns are permitted to contain globbing characters (*, ?, [)
# supported by dash
match_machine() {
local machine="$1"
local pattern="$2"
case "$machine" in
$pattern) # Must not be quoted to permit globbing
return 0
;;
*)
return 1
;;
esac
}
# this is case-sensitive and doesn't support fields spanning multiple lines
get_machine_field() {
local machine="$1"
local field_name="$2"
local state="machine"
local field
local value
# State machine is:
# machine: Looking for "Machine:"
# gotmachine: Seen matching "Machine:", possibly consumning other
# alternative (non-matching) "Machine:"s waiting for
# non-"Machine:" field.
# fields: seen non-"Machine:" field.
echo "$MACHINE_DB" | {
while read field value; do
if [ "$state" = "machine" ] &&
[ "$field" = "Machine:" ] &&
match_machine "$machine" "$value"; then
state="gotmachine"
fi
if [ "$state" = "fields" ] || [ "$state" = "gotmachine" ]; then
case "$field" in
"${field_name}:")
echo "$value"
return 0
;;
Machine:)
if [ "$state" != "gotmachine" ]; then
echo "DB syntax invalid, got $field $value, expected regular Field" >&2
return 1
fi
;;
"")
state="machine"
;;
*)
state="fields"
;;
esac
fi
done
return 1
}
}
get_mkimage_architecture() {
kfile_suffix=$(get_kfile_suffix "$1" "")
case "$kfile_suffix" in
"arm64") echo "arm64";;
*) echo "arm";;
esac
}
get_dtb_name() {
local field="$(get_machine_field "$machine" "DTB-Id")" || :
case "$field" in
!*)
local dir
local dtb_script
dtb_script_name=${field#!}
for dir in /etc/flash-kernel/dtb-probe \
/usr/share/flash-kernel/dtb-probe ; do
if [ -e "$dir/$dtb_script_name" ]; then
dtb_script="$dir/$dtb_script_name"
break
fi
done
if [ "x$dtb_script" = "x" ]; then
error "dtb-probe $dtb_script_name not found"
fi
dtb_name=$($dtb_script $kvers)
if [ $? -ne 0 ] || [ "x$dtb_name" = "x" ]; then
error "dtb-probe $dtb_script failed"
fi
;;
*)
dtb_name="$field"
;;
esac
if [ -n "$dtb_name" ] ; then
echo "Using DTB: $dtb_name" >&2
fi
dtb_dir=$(dirname "$dtb_name")
dtb_name=$(basename "$dtb_name")
}
machine_uses_flash() {
local machine="$1"
if ! check_supported "$machine"; then
# assume devices not explicitly listed are using flash
return 0
fi
if [ -n "$(get_machine_field "$machine" "Mtd-Kernel")" ] ||
[ -n "$(get_machine_field "$machine" "Mtd-Initrd")" ]; then
return 0
fi
return 1
}
# output ARM instructions to set machine number; argument is the decimal
# machine number as found in linux/arch/arm/tools/mach-types
set_machine_id() {
local machine_id="$1"
local high
local low
if [ -z "$machine_id" ]; then
return
fi
high="$(printf "%02x" $(($machine_id / 256)))"
low="$(printf "%02x" $(($machine_id % 256)))"
devio "wl 0xe3a01c$high,4" "wl 0xe38110$low,4"
}
gen_kernel() {
local input="$1"
local output="$2"
local machine_id="$3"
{
set_machine_id "$machine_id"
cat "$input"
} >"$output"
}
gen_preboot() {
PRESTUBDIRS="/etc/flash-kernel/preboot.d /usr/share/flash-kernel/preboot.d"
PRESTUBS="$(find $PRESTUBDIRS -type f -regex '.*/[0-9a-zA-Z_-]+' -printf '%f\n' | LC_ALL=C sort -u)"
for file in $PRESTUBS; do
for dir in $PRESTUBDIRS; do
if [ -f $dir/$file ]; then
cat $dir/$file
break
fi
done
done
}
gen_ubootenv() {
ENVSTUBDIRS="/etc/flash-kernel/ubootenv.d /usr/share/flash-kernel/ubootenv.d"
ENVSTUBS="$(find $ENVSTUBDIRS -type f -regex '.*/[0-9a-zA-Z_-]+' -printf '%f\n' | LC_ALL=C sort -u)"
for file in $ENVSTUBS; do
for dir in $ENVSTUBDIRS; do
if [ -f $dir/$file ]; then
cat $dir/$file
break
fi
done
done
}
append_dtb() {
local kernel="$1"
local dtb="$2"
local output="$3"
echo "flash-kernel: appending $dtb to kernel" >&2
{
cat "$kernel"
cat "$dtb"
} >"$output"
}
write_mtd() {
local input_file="$1"
local output_mtd="$2"
local base_mtd=$(basename $output_mtd)
local mtd_backup_dir=$(get_mtd_backup_dir)
local tmpfile
if [ "x$input_file" = "x-" ] ; then
tmpfile=$(mktemp -t "$self.$base_mtd.XXXXXX") || error "Failed"
cat > $tmpfile
input_file="$tmpfile"
fi
# Keep a copy in a convenient place for backups etc
if [ -n "${mtd_backup_dir}" ] ; then
local backup="${mtd_backup_dir}/${base_mtd}"
#echo "Saving new $output_mtd in ${backup}."
mkdir -p "${mtd_backup_dir}"
cp "$input_file" "${backup}"
fi
# Can't really flashcp to /dev/mtd<N> when testing
if [ -z "${FK_TESTSUITE_RUNNING}" ]; then
local flashtype=$(cat /sys/class/mtd/$base_mtd/type)
case "$flashtype" in
nand|mlc-nand)
flash_erase "$output_mtd" 0 0
nandwrite -p "$output_mtd" "$input_file"
;;
nor)
flashcp "$input_file" "$output_mtd"
;;
*)
error "unsupported flash type"
;;
esac
else
cp "$input_file" "$output_mtd"
fi
if [ "$tmpfile" ] ; then
rm -f "$tmpfile"
fi
}
flash_kernel() {
local input_file="$1"
local output_mtd="$2"
local machine_id="$3"
local use
if [ -n "$kmtdsize" ]; then
kreqsize=$(stat -c '%s' "$input_file")
if [ -n "$machine_id" ]; then
kreqsize=$(($kreqsize + 8))
fi
check_mtd_size "$mtd_kernel" $kreqsize $kmtdsize kernel
use=" (using $kreqsize/$kmtdsize bytes)"
fi
printf "Flashing kernel$use... " >&2
gen_kernel "$input_file" "$tmpdir/flash_kernel.raw" "$machine_id" || error "failed."
write_mtd "$tmpdir/flash_kernel.raw" "$output_mtd" || error "failed."
echo "done." >&2
}
flash_initrd() {
local input_file="$1"
local output_mtd="$2"
local pad="$3"
local use
if [ -n "$imtdsize" ]; then
use=" (using $ireqsize/$imtdsize bytes)"
fi
printf "Flashing initramfs$use... " >&2
{
cat "$input_file"
if [ "$pad" -gt 0 ]; then
dd if=/dev/zero bs="$pad" count=1 2>/dev/null
fi
} | write_mtd "-" "$output_mtd" || error "failed."
echo "done." >&2
}
compress_type() {
local file="$1"
magic="$(od -x -N2 $file | head -1 | cut -d' ' -f2)"
case $magic in
8b1f)
echo "gzip"
;;
*)
echo "none"
;;
esac
}
get_kernel_cmdline() {
. ${FK_DEFAULTS}
echo "$LINUX_KERNEL_CMDLINE"
}
get_kernel_cmdline_defaults() {
. ${FK_DEFAULTS}
echo "$LINUX_KERNEL_CMDLINE_DEFAULTS"
}
maybe_defrag() {
local file="$1"
local field="Bootloader-Has-Broken-Ext4-Extent-Support"
local broken_fw
if ! broken_fw=$(get_machine_field "$machine" "$field"); then
return
fi
if [ "$broken_fw" != "yes" ]; then
return
fi
if [ "$(df --output=fstype ${file} | sed -e 1d)" != "ext4" ]; then
return
fi
if ! command -v e4defrag > /dev/null; then
printf "WARNING: e4defrag command not found, unable to defrag %s.\n" "$file"
printf "WARNING: THIS MAY LEAVE YOUR SYSTEM UNBOOTABLE.\n"
return
fi
if ! e4defrag "$file" > /dev/null 2>&1; then
printf "WARNING: e4defrag of file %s failed.\n" "$file"
printf "WARNING: Try freeing up disk space in /boot and retrying flash-kernel.\n"
fi
}
mkimage_kernel() {
local kaddr="$1"
local epoint="$2"
local kdesc="$3"
local kdata="$4"
local uimage="$5"
local comp
comp="$(compress_type $kdata)"
printf "Generating kernel u-boot image... " >&2
mkimage -A "$mkarch" -O linux -T kernel -C $comp -a "$kaddr" -e "$epoint" \
-n "$kdesc" -d "$kdata" "$uimage" >&2 1>/dev/null
echo "done." >&2
}
mkimage_initrd() {
local iaddr="$1"
local idesc="$2"
local idata="$3"
local uinitrd="$4"
printf "Generating initramfs u-boot image... " >&2
mkimage -A "$mkarch" -O linux -T ramdisk -C none -a "$iaddr" -e "$iaddr" \
-n "$idesc" -d "$idata" "$uinitrd" >&2 1>/dev/null
echo "done." >&2
}
mkimage_script() {
local saddr="$1"
local sdesc="$2"
local sdata="$3"
local script="$4"
local tdata="$tmpdir/$(basename $sdata).out"
local ubootenv="$(mktemp --tmpdir=$tmpdir)"
gen_ubootenv > $ubootenv
local preboot="$(mktemp --tmpdir=$tmpdir)"
gen_preboot > $preboot
if [ "$(stat --printf='%s' $ubootenv)" -gt 0 ] && \
! grep -q '@@UBOOT_ENV_EXTRA@@' "$sdata" ; then
echo "WARNING: ubootenv.d snippet used, but $sdata has no @@UBOOT_ENV_EXTRA@@ marker. Snippet will be ignored." >&2
fi
printf "Generating boot script u-boot image... " >&2
sed -e "s/@@KERNEL_VERSION@@/$kvers/g" \
-e "s!@@LINUX_KERNEL_CMDLINE@@!$(get_kernel_cmdline)!g" \
-e "s!@@LINUX_KERNEL_CMDLINE_DEFAULTS@@!$(get_kernel_cmdline_defaults)!g" \
-e "/@@UBOOT_ENV_EXTRA@@/{
s/@@UBOOT_ENV_EXTRA@@//g
r $ubootenv
}" \
-e "/@@UBOOT_PREBOOT_EXTRA@@/{
s/@@UBOOT_PREBOOT_EXTRA@@//g
r $preboot
}" < $sdata > $tdata
mkimage -A "$mkarch" -O linux -T script -C none -a "$saddr" -e "$saddr" \
-n "$sdesc" -d "$tdata" "$script" >&2 1>/dev/null
echo "done." >&2
}
mkimage_multi() {
local maddr="$1"
local mdesc="$2"
local kdata="$3"
local idata="$4"
local umulti="$5"
local images="$kdata"
[ -z "$idata" ] || images="$images:$idata"
printf "Generating u-boot image..." >&2
mkimage -A "$mkarch" -O linux -T multi -C none -a "$maddr" -e "$maddr" \
-n "$mdesc" -d "$images" "$umulti" >&2 1>/dev/null
echo "done." >&2
}
mkimage_fit() {
local image_its="$1"
local image_fit="$2"
local kernel_file="$3"
local initrd_file="$4"
local kernel_ver="$5"
local dtb_file="$6"
local its_tmp="$tmpdir/image.its"
sed -e "s#@@LINUX_IMAGE_FILE@@#$kernel_file#g" \
-e "s#@@INITRD_FILE@@#$initrd_file#g" \
-e "s#@@DEVICE_TREE_FILE@@#$dtb_file#g" \
-e "s#@@KERNEL_VERSION@@#$kernel_ver#g" \
< "$ITSFILES_DIR/$image_its" > "$its_tmp"
printf "Generating u-boot image..." >&2
mkimage -D "-I dts -O dtb" -f "$its_tmp" "$image_fit" >&2 1>/dev/null
echo " done." >&2
}
create_boot_script() {
case $usname in
bootscr*)
boot_script_path="$boot_mnt_dir/$boot_script_path"
boot_script="$tmpdir/bootscript"
for script in $usname ; do
echo "\n#\n# flash-kernel: $script\n#\n" >> "$boot_script"
cat "$BOOTSCRIPTS_DIR/$script" >> "$boot_script"
done
mkimage_script "$usaddr" "boot script" "$boot_script" \
"$tmpdir/boot.scr"
boot_script="$tmpdir/boot.scr"
backup_and_install "$boot_script" "$boot_script_path"
;;
uEnvtxt*)
VOLID=${VOLID:-"$(get_root_uuid)"}
boot_script_in="$BOOTSCRIPTS_DIR/$usname"
boot_script="$boot_mnt_dir/$usname"
cp $boot_script_in $boot_script
boot_script_path="$boot_mnt_dir/uEnv.txt"
boot_script_env="$boot_mnt_dir/preEnv.txt"
env_script="$tmpdir/preEnv.txt"
[ -e /etc/default/flash-kernel ] && . /etc/default/flash-kernel
echo "bootargs=ro $(LINUX_KERNEL_CMDLINE) root=UUID=$VOLID" > $env_script
backup_and_install "$env_script" "$boot_script_env"
backup_and_install "$boot_script" "$boot_script_path"
;;
esac
}
# Return a nonempty string *unless* NO_CREATE_DOT_BAK_FILES is set.
get_dot_bak_preference() {
. ${FK_DEFAULTS}
case $(echo "$NO_CREATE_DOT_BAK_FILES" | tr '[:upper:]' '[:lower:]') in
true|yes|1) ;;
*) echo yes ;;
esac
}
# Return a nonempty string *unless* MTD_BACKUP_DIR is "none".
get_mtd_backup_dir() {
. ${FK_DEFAULTS}
case "${MTD_BACKUP_DIR:=/var/backups/flash-kernel}" in
none) echo "";;
*) echo "${MTD_BACKUP_DIR}";;
esac
}
backup_and_install() {
local source="$1"
local dest="$2"
local do_dot_bak=$(get_dot_bak_preference)
local mtd_backup_dir=$(get_mtd_backup_dir)
if [ -e "$dest" ]; then
if [ -n "$do_dot_bak" ]; then
echo "Taking backup of $(basename "$dest")." >&2
mv "$dest" "$dest.bak"
else
echo "Skipping backup of $(basename "$dest")." >&2
fi
fi
# If we are installing to a filesystem which is not normally mounted
# then take a second copy in /var/backups, where they can e.g. be
# backed up.
if [ -n "$boot_mnt_dir" ] && [ -n "$mtd_backup_dir" ] ; then
local bak="$mtd_backup_dir/"$(basename "$dest")
#echo "Saving $boot_device:"$(basename "$source")" in $bak"
mkdir -p "$mtd_backup_dir"
cp "$source" "$bak"
fi
echo "Installing new $(basename "$dest")." >&2
mv "$source" "$dest"
maybe_defrag "$dest"
}
backup_and_remove() {
local dest="$1"
local do_dot_bak=$(get_dot_bak_preference)
if [ -e "$dest" ]; then
if [ -n "$do_dot_bak" ]; then
echo "Taking backup of $(basename "$dest")." >&2
mv "$dest" "$dest.bak"
else
echo "Skipping backup of $(basename "$dest")." >&2
rm "$dest"
fi
fi
}
# See http://www.nslu2-linux.org/wiki/Info/BootFlash -- the NSLU2 uses a
# 16 byte MTD header, the first four bytes (big endian) give the length of
# the remainder of the image, and the remaining bytes are zero. Generate
# this header.
sercomm_header() {
perl -e 'print pack("N4", shift)' "$1"
}
nslu2_swap() {
if [ "$little_endian" -eq 1 ]; then
devio "<<$1" "xp $,4"
else
cat "$1"
fi
}
abootimg_get_image_size() {
local abootimg="$1"
echo "$abootimg" | sed -rn 's/^\* image size = ([0-9]+) bytes.*/\1/p'
}
dtb_append_required() {
linux-version compare "$kvers" ge "$dtb_append_from"
}
android_flash() {
local device="$1"
if [ -z "$android_skip_initrd" ]; then
printf "Flashing kernel and initramfs to $device... " >&2
abootimg -u "$device" -k "$kfile" -r "$ifile" >/dev/null ||
error "failed."
echo "done." >&2
else
printf "Flashing the kernel (skipping initrd) to $device... " >&2
abootimg -u "$device" -k "$kfile" >/dev/null || error "failed."
echo "done." >&2
fi
}
include_only_flavors() {
# include_only_flavors(flav1, flav2, flav3)
# filter lines of input in uname -r format (X.Y.Z-N-flavor)
# and filter-out anything that is not in the listed input
# if the only flavor given is "any", then assume everything is a match
local cur_uname cur_flav allowed_flav
while read cur_uname; do
if [ "$*" = "any" ]; then
echo "$cur_uname"
else
# could use cur_flav=$(get_kfile_suffix "$cur_uname")
# but this is much faster.
cur_flav=${cur_uname#*-*-}
for allowed_flav in "$@"; do
if [ "${cur_flav}" = "${allowed_flav}" ]; then
echo "$cur_uname"
break
fi
done
fi
done
}
find_dtb_file() {
local dtb path
case "$dtb_dir" in
/*)
dtb="$dtb_dir/$dtb_name"
if [ ! -f "$dtb" ]; then
error "Couldn't find $dtb"
fi
;;
*)
dtb=
paths="/etc/flash-kernel/dtbs /usr/lib/linux-image-$kvers /lib/firmware/$kvers/device-tree/"
for path in $paths; do
dtb=$(find $path -name $dtb_name 2>/dev/null | head -n 1)
[ -z "$dtb" ] || break
done
if [ ! -f "$dtb" ]; then
error "Couldn't find DTB $dtb_name on the following paths: $paths"
fi
;;
esac
echo $dtb
}
unique_filenames() {
awk '
{
file=$0;
sub("^.*/", "", file);
if (!(file in files)) files[file]=$0;
}
END {
for (file in files) print files[file];
}'
}
find_all_dtbs() {
# NOTE: The following assumes that "find" processes and outputs
# filenames in the same order as starting points ($paths) are specified
paths="/etc/flash-kernel/dtbs /usr/lib/linux-image-$kvers /lib/firmware/$kvers/device-tree/"
find $paths -name "*.dtb" 2>/dev/null | unique_filenames
}
find_all_overlays() {
paths="/etc/flash-kernel/dtbs /usr/lib/linux-image-$kvers /lib/firmware/$kvers/device-tree/"
find $paths -name "*.dtbo" 2>/dev/null | unique_filenames
}
find_pi_firmware() {
paths="/usr/lib/linux-firmware-raspi /usr/lib/linux-firmware-raspi2"
find $paths -name "*.bin" -o -name "*.elf" -o -name "*.dat" 2>/dev/null | unique_filenames
}
handle_dtb() {
if [ "x$dtb_name" = "x" ]; then
return
fi
dtbfile="/boot/dtbs/$kvers/$dtb_dir/$dtb_name"
local dtb
if [ "x$FK_KERNEL_HOOK_SCRIPT" = "xpostrm.d" ] ; then
rm -f "$dtbfile"
# This was the old name we installed under. We
# currently include it as an alternative symlink.
rm -f "/boot/dtb-$kvers"
if [ -L /boot/dtb ] ; then
# Remove if it points to the current kernel
# version, both old (dtb-$kvers) and new
# (dtbs/...) names
case $(readlink /boot/dtb) in
"dtb-$kvers") rm -f /boot/dtb ;;
"dtbs/$kvers/$dtb_name") rm -f /boot/dtb ;;
esac
fi
if [ -d /boot/dtbs/$kvers ] ; then
rmdir --ignore-fail-on-non-empty /boot/dtbs/$kvers
fi
if [ -d /boot/dtbs ] ; then
rmdir --ignore-fail-on-non-empty /boot/dtbs
fi
else
local dtb=$(find_dtb_file)
echo "Installing $dtb into $dtbfile" >&2
mkdir -p "/boot/dtbs/$kvers/$dtb_dir"
cp "$dtb" "$dtbfile.new"
backup_and_install "$dtbfile.new" "$dtbfile"
if [ "$dtb_dir" != "." ]; then
ln -nfs "$dtb_dir/$dtb_name" "/boot/dtbs/$kvers/$dtb_name"
fi
# Historically we installed the dtb as
# dtb-$kvers, keep it around as an alternative
# for now. Useful for platforms which do not
# set ${fdtfile}
ln -nfs "dtbs/$kvers/$dtb_dir/$dtb_name" "/boot/dtb-$kvers"
# This can be used along with the unversioned
# vmlinuz+initrd.gz e.g. as a fallback option
ln -nfs "dtbs/$kvers/$dtb_dir/$dtb_name" "/boot/dtb"
fi
}
main() {
# Do not run inside an LXC container
if systemd-detect-virt --quiet --container; then
exit 0
fi
force="no"
if [ "x$1" = "x--force" ]; then
force="yes"
shift
fi
if [ "x$1" = "x--machine" ]; then
machine="$2"
shift 2
else
get_machine
fi
if [ "x$1" = "x--supported" ]; then
if check_supported "$machine"; then
exit 0
fi
exit 1
fi
# $FK_KERNEL_HOOK_SCRIPT is set when main() is called from
# /etc/kernel/* to be able to differentiate between being called
# upon kernel installation or kernel removal
# kernel + initrd installation/upgrade mode, with optional version
kvers="$1"
get_dtb_name
# Install/remove any DTB from postinst, regardless of version
if [ -n "$kvers" ] ; then
handle_dtb
fi
# if get_machine_field returns non-zero, then all flavors are allowed
if ! kflavors=$(get_machine_field "$machine" "Kernel-Flavors") ; then
# Since no Kernel-Flavors were specified, allow any kernel.
kflavors="any"
fi
latest_version=$(linux-version list | include_only_flavors $kflavors | linux-version sort | tail -1)
if [ -n "$kvers" ] && [ "$FK_KERNEL_HOOK_SCRIPT" = "postrm.d" ]; then
echo "flash-kernel: Kernel ${kvers} has been removed." >&2
if $(linux-version compare "$kvers" lt "$latest_version"); then
echo "flash-kernel: A higher version (${latest_version}) is still installed, no reflashing required." >&2
exit 0
else
if [ -n "${latest_version}" ]; then
echo "flash-kernel: Flashing the remaining highest-versioned kernel (${latest_version})." >&2
else
echo "flash-kernel: WARNING: No other kernel packages found!" >&2
echo "flash-kernel: The system might be unbootable." >&2
echo "flash-kernel: Please install a kernel package before rebooting the system." >&2
exit 0
fi
fi
fi
if [ "$kvers" != "$latest_version" ] && [ "x$force" = "xyes" ]; then
echo "flash-kernel: forcing install of ${kvers} instead of ${latest_version}." >&2
echo "flash-kernel: WARNING: Installing any new kernel package might override this." >&2
else
if [ -n "$kvers" ] && [ "$kvers" != "$latest_version" ]; then
echo "Ignoring old or unknown version $kvers (latest is $latest_version)" >&2
exit 0
fi
kvers="$latest_version"
# Make sure we install the DTB for $latest_version
handle_dtb
fi
# accumulate multiple calls in a trigger to only run flash-kernel once; the
# trigger will just call flash-kernel again with FLASH_KERNEL_NOTRIGGER set to
# force a real run
if [ -z "$FLASH_KERNEL_NOTRIGGER" ] && [ -n "$DPKG_MAINTSCRIPT_PACKAGE" ] && dpkg-trigger --check-supported 2>/dev/null; then
# flash-kernel trigger isn't disabled, and we're called from
# some package maintainer script (e.g. some postinst calls
# flash-kernel), and dpkg-trigger is installed and confirms
# that the running dpkg support triggers: we can use the
# flash-kernel trigger (asynchronously)
if dpkg-trigger --no-await flash-kernel; then
echo "flash-kernel: deferring update (trigger activated)" >&2
exit 0
fi
# dpkg-trigger failed for some reason, proceed to a normal run
fi
kfile="/boot/vmlinuz-$kvers"
ifile="/boot/initrd.img-$kvers"
desc="kernel $kvers"
idesc="ramdisk $kvers"
if [ ! -e "$ifile" ]; then
ifile=
fi
if [ ! -e $kfile ]; then
# installation-report #781742 included:
# Can't find /boot/vmlinuz-[...] or /boot/initrd.img-[...]
#
# It's unclear how this can have happened or what state the
# system was in, so log some additional information in the
# hopes of catching it in the act next time.
(
set -x
ls -l $kfile*
ls -l $ifile*
) >> /tmp/flash-kernel-no-kernel-error.log 2>&1 || :
error "Can't find $kfile (see /tmp/flash-kernel-no-kernel-error.log)"
fi
kfilesize=$(stat -c '%s' "$kfile")
[ -z "$ifile" ] || ifilesize=$(stat -c '%s' "$ifile")
if [ -L "$kfile" ]; then
kfile=$(readlink -e "$kfile")
fi
if ! check_supported "$machine"; then
# do nothing if an unsupported platform is booted from EFI
if [ -d /sys/firmware/efi ]; then
echo "Unsupported platform on EFI system, doing nothing."
exit 0
fi
error "Unsupported platform."
fi
kfile_suffix=""
for kflavor in $kflavors ; do
if check_kflavors "$kvers" "$kflavor" ; then
kfile_suffix="$kflavor"
break
fi
done
if [ -z "$kfile_suffix" ]; then
echo "Kernel $kfile does not match any of the expected flavors ($kflavors), therefore not writing it to flash." >&2
exit 0
fi
echo "flash-kernel: installing version $kvers" >&2
mkarch="$(get_mkimage_architecture $kvers)"
machine_id="$(get_machine_field "$machine" "Machine-Id")" || :
method="$(get_machine_field "$machine" "Method")" || method="generic"
mtd_kernel="$(get_machine_field "$machine" "Mtd-Kernel")" || :
mtd_initrd="$(get_machine_field "$machine" "Mtd-Initrd")" || :
dtb_append="$(get_machine_field "$machine" "DTB-Append")" || :
dtb_append_from="$(get_machine_field "$machine" "DTB-Append-From")" || :
ukaddr="$(get_machine_field "$machine" "U-Boot-Kernel-Address")" || :
ukepoint="$(get_machine_field "$machine" "U-Boot-Kernel-Entry-Point")" || :
uiaddr="$(get_machine_field "$machine" "U-Boot-Initrd-Address")" || :
umaddr="$(get_machine_field "$machine" "U-Boot-Multi-Address")" || :
usaddr="$(get_machine_field "$machine" "U-Boot-Script-Address")" || :
usname="$(get_machine_field "$machine" "U-Boot-Script-Name")" || :
boot_device="$(get_machine_field "$machine" "Boot-Device")" || :
boot_kernel_path="$(get_machine_field "$machine" "Boot-Kernel-Path")" || :
boot_initrd_path="$(get_machine_field "$machine" "Boot-Initrd-Path")" || :
boot_kernel_path_version="$(get_machine_field "$machine" "Boot-Kernel-Path-Version")" || :
boot_initrd_path_version="$(get_machine_field "$machine" "Boot-Initrd-Path-Version")" || :
boot_script_path="$(get_machine_field "$machine" "Boot-Script-Path")" || :
boot_dtb_path="$(get_machine_field "$machine" "Boot-DTB-Path")" || :
boot_dtb_path_version="$(get_machine_field "$machine" "Boot-DTB-Path-Version")" || :
boot_fit_path="$(get_machine_field "$machine" "Boot-FIT-Path")" || :
boot_its_file_name="$(get_machine_field "$machine" "Boot-ITS-File-Name")" || :
boot_multi_path="$(get_machine_field "$machine" "Boot-Multi-Path")" || :
android_boot_device="$(get_machine_field "$machine" "Android-Boot-Device")" || :
android_skip_initrd="$(get_machine_field "$machine" "Android-Skip-Initrd")" || :
if [ -n "$dtb_append_from" ]; then
if dtb_append_required; then
dtb_append="yes"
else
dtb_append="no"
fi
fi
if [ -n "$mtd_kernel" ] || [ -n "$mtd_initrd" ]; then
if [ ! -e "$PROC_MTD" ]; then
error "$PROC_MTD doesn't exist"
fi
fi
if [ -n "$mtd_kernel" ]; then
kmtd=$(mtdchar "$mtd_kernel")
if [ -z "$kmtd" ]; then
error "Cannot find mtd partition '$mtd_kernel'"
fi
check_char_dev "$kmtd"
kmtdsize=$(mtdsize "$mtd_kernel")
kreqsize=$kfilesize
check_mtd_size "$mtd_kernel" $kreqsize $kmtdsize kernel
fi
if [ -n "$mtd_initrd" ]; then
imtd=$(mtdchar "$mtd_initrd")
if [ -z "$imtd" ]; then
error "Cannot find mtd partition '$mtd_initrd'"
fi
check_char_dev "$imtd"
imtdsize=$(mtdsize "$mtd_initrd")
ireqsize=$ifilesize
# encapsulating in an U-Boot image grows the size by 64 bytes
if [ -n "$uiaddr" ]; then
ireqsize=$(($ireqsize + 64))
fi
check_mtd_size "$mtd_initrd" $ireqsize $imtdsize initrd
fi
tmpdir=""
boot_mnt_dir=""
cleanups() {
rm -rf "$tmpdir"
if [ -d "$boot_mnt_dir" ]; then
umount -l "$boot_mnt_dir"
rmdir "$boot_mnt_dir"
fi
}
trap cleanups EXIT HUP INT QUIT ILL KILL SEGV PIPE TERM
self="$(basename "$0")"
tmpdir="$(mktemp -dt "$self.XXXXXXXX")"
case "$method" in
"android")
[ -n "$ifile" ] || error "Initrd required for android method"
if abootimg -i "$android_boot_device" > /dev/null 2>&1; then
part="$android_boot_device"
else
part=""
largest_size="-1"
for p in "$android_boot_device"*[0-9]; do
if ! abootimg="$(LC_ALL=C abootimg -i "$p" 2>/dev/null)"; then
continue
fi
image_size="$(abootimg_get_image_size "$abootimg")"
if [ -n "$image_size" ] &&
[ "$image_size" -gt "$largest_size" ]; then
part="$p"
fi
done
fi
if [ -z "$part" ]; then
error "Couldn't find Android boot partition on $android_boot_device"
fi
android_flash "$part"
;;
"bootspec")
{
printf "title\t\tDebian\n"
printf "version\t\t$(cat /etc/debian_version) ($desc)\n"
printf "linux\t\t$kfile\n"
[ -z "$ifile" ] || printf "initrd\t\t$ifile\n"
if [ -n "$dtb_name" ] ; then
printf "devicetree\t$dtbfile\n"
fi
printf "options\t\t$(get_kernel_cmdline)\n"
printf "linux-appendroot true\n"
} > "$tmpdir/debian.conf"
mkdir -p /loader/entries
mv "$tmpdir/debian.conf" /loader/entries/debian.conf
;;
"generic")
kernel="$kfile"
initrd="$ifile"
if [ "$dtb_append" = "yes" ]; then
dtb=$(find_dtb_file)
append_dtb "$kernel" "$dtb" "$tmpdir/kernel"
kernel="$tmpdir/kernel"
elif [ -n "$machine_id" ]; then
gen_kernel "$kernel" "$tmpdir/kernel" "$machine_id"
kernel="$tmpdir/kernel"
fi
if [ -n "$ukaddr" ]; then
if [ -n "$ukepoint" ]; then
mkimage_kernel "$ukaddr" "$ukepoint" "$desc" "$kernel" \
"$tmpdir/uImage"
else
mkimage_kernel "$ukaddr" "$ukaddr" "$desc" "$kernel" \
"$tmpdir/uImage"
fi
kernel="$tmpdir/uImage"
rm -f "$tmpdir/kernel"
fi
if [ -n "$umaddr" ]; then
mkimage_multi "$umaddr" "$desc" "$kernel" "$initrd" \
"$tmpdir/uImage"
rm -f "$tmpdir/kernel"
fi
if [ -n "$boot_fit_path" ]; then
[ -n "$ifile" ] || error "Initrd required for FIT method"
# Write tmp file in same filesystem as final destination
fit_tmp="$boot_fit_path".tmp
mkimage_fit "$boot_its_file_name" "$fit_tmp" \
"$kfile" "$ifile" "${kvers}" "$(find_dtb_file)"
backup_and_install "$fit_tmp" "$boot_fit_path"
fi
if [ -n "$boot_device" ]; then
check_block_dev "$boot_device"
echo "Will use $boot_device as boot device." >&2
# don't use $tmpdir/boot as to not nuke it accidentally
# if umount fails
boot_mnt_dir="$(mktemp -dt "$self.XXXXXXXX")"
mount "$boot_device" "$boot_mnt_dir"
fi
if [ -n "$boot_kernel_path" ]; then
boot_kernel_path="$boot_mnt_dir/$boot_kernel_path"
if [ "$boot_kernel_path_version" = "yes" ]; then
boot_kernel_path="${boot_kernel_path}-${kvers}"
fi
# don't mv the original kernel
if [ "$kernel" = "$kfile" ]; then
cp "$kernel" "$tmpdir/kernel"
kernel="$tmpdir/kernel"
fi
backup_and_install "$kernel" "$boot_kernel_path"
elif [ -n "$kmtd" ]; then
flash_kernel "$tmpdir/uImage" "$kmtd" ""
rm -f "$tmpdir/uImage"
fi
if [ -n "$boot_multi_path" ]; then
backup_and_install "$tmpdir/uImage" "$boot_multi_path"
fi
if [ -n "$uiaddr" ]; then
[ -n "$ifile" ] || error "Initrd required for generic method with Initrd-Adress"
mkimage_initrd "$uiaddr" "$idesc" "$initrd" \
"$tmpdir/uInitrd"
initrd="$tmpdir/uInitrd"
fi
if [ -n "$boot_initrd_path" ]; then
[ -n "$ifile" ] || error "Initrd required for generic method with Boot-Initrd-Path"
boot_initrd_path="$boot_mnt_dir/$boot_initrd_path"
if [ "$boot_initrd_path_version" = "yes" ]; then
boot_initrd_path="${boot_initrd_path}-${kvers}"
fi
# don't mv the original initrd
if [ "$initrd" = "$ifile" ]; then
cp "$initrd" "$tmpdir/initrd"
initrd="$tmpdir/initrd"
fi
backup_and_install "$initrd" "$boot_initrd_path"
elif [ -n "$imtd" ]; then
[ -n "$ifile" ] || error "Initrd required for generic method with Mtd-Initrd"
ipad=0
# padding isn't needed for U-Boot images
if [ -z "$uiaddr" ]; then
ipad=$(($imtdsize - $ireqsize))
fi
flash_initrd "$initrd" "$imtd" $ipad
rm -f "$tmpdir/uInitrd"
fi
if [ -n "$boot_script_path" ]; then
create_boot_script
fi
if [ -n "$boot_dtb_path" ] && [ "$dtb_append" != "no" ]; then
boot_dtb_path="$boot_mnt_dir/$boot_dtb_path"
if [ "$boot_dtb_path_version" = "yes" ]; then
boot_dtb_path="${boot_dtb_path}-${kvers}"
fi
boot_dtb=$(find_dtb_file)
dtb="$tmpdir/dtb"
cp "$boot_dtb" "$dtb"
backup_and_install "$dtb" "$boot_dtb_path"
fi
;;
"pi")
boot_dir="/boot/firmware"
boot_kernel_path=${boot_kernel_path:-$boot_dir/vmlinuz}
boot_initrd_path=${boot_initrd_path:-$boot_dir/initrd.img}
boot_script_path=${boot_script_path:-$boot_dir/boot.scr}
cp "$kfile" "$tmpdir/vmlinuz"
backup_and_install "$tmpdir/vmlinuz" "$boot_kernel_path"
if [ -n "$ifile" ]; then
cp "$ifile" "$tmpdir/initrd.img"
backup_and_install "$tmpdir/initrd.img" "$boot_initrd_path"
else
backup_and_remove "$boot_initrd_path"
fi
if [ -n "$usname" ]; then
boot_script="$tmpdir/boot.scr"
for script in $usname ; do
echo "\n#\n# flash-kernel: $script\n#\n" >> "$boot_script"
cat "$BOOTSCRIPTS_DIR/$script" >> "$boot_script"
done
mkimage_script "$usaddr" "boot script" "$boot_script" "$tmpdir/boot.scr"
backup_and_install "$boot_script" "$boot_dir/boot.scr"
fi
find_pi_firmware | {
while read fw; do
fw_name=$(basename $fw)
cp "$fw" "$tmpdir/$fw_name"
backup_and_install "$tmpdir/$fw_name" "$boot_dir/$fw_name"
done
}
find_all_dtbs | {
while read dtb; do
dtb_name=$(basename $dtb)
cp "$dtb" "$tmpdir/$dtb_name"
backup_and_install "$tmpdir/$dtb_name" "$boot_dir/$dtb_name"
done
}
mkdir -p "$boot_dir/overlays"
find_all_overlays | {
while read overlay; do
overlay_name=$(basename $overlay)
cp "$overlay" "$tmpdir/$overlay_name"
backup_and_install "$tmpdir/$overlay_name" "$boot_dir/overlays/$overlay_name"
done
}
;;
"symlink")
[ -n "$ifile" ] || error "Initrd required for symlink method"
rm -f /boot/initrd /boot/zImage
ln -s "$(basename "$ifile")" /boot/initrd
gen_kernel "$kfile" "/boot/zImage" "$machine_id"
;;
"multi")
[ -n "$ifile" ] || error "Initrd required for multi method"
gen_kernel "$kfile" "$tmpdir/kernel" ""
# Hack to work around a bug in some U-Boot versions:
if [ $(($(stat -c '%s' "$tmpdir/kernel") % 4)) -eq 0 ]; then
echo >> "$tmpdir/kernel"
fi
mkimage_multi "$umaddr" "$desc" "$tmpdir/kernel" "$ifile" \
"$tmpdir/uImage"
rm -f "$tmpdir/kernel"
backup_and_install "$tmpdir/uImage" "$boot_multi_path"
;;
"redboot")
[ -n "$ifile" ] || error "Initrd required for redboot method"
flash_kernel "$kfile" "$kmtd" "$machine_id"
pad=$(($imtdsize - $ifilesize))
flash_initrd "$ifile" "$imtd" $pad
;;
"slug")
[ -n "$ifile" ] || error "Initrd required for slug method"
case "$(dpkg --print-architecture)" in
arm|armel)
little_endian=1
;;
armeb)
little_endian=0
;;
esac
mtd_fis="FIS directory"
fismtd=$(mtdblock "$mtd_fis")
if [ -z "$fismtd" ]; then
error "Cannot find mtd partition '$mtd_fis'"
fi
check_mtd_size "$mtd_kernel" $(($kfilesize + 16 + 16)) \
$kmtdsize kernel
check_mtd_size "$mtd_initrd" $(($ifilesize + 16)) \
$imtdsize initrd
# The following devio magic parses the FIS directory to
# obtain the size, offset and name of each partition. This
# used used to obtain the offset of the Kernel partition.
offset=$(echo "$(devio "<<$fismtd" '
<= $ 0x20000 -
L= 0x1000
$( 1
# 0xff byte in name[0] ends the partition table
$? @ 255 =
# output size base name
<= f15+
.= b 0xfffffff &
<= f4+
.= b
pf "%lu %lu "
<= f28-
cp 16
pn
<= f240+
L= L256-
$) L255>')" |
while read a b c; do
if [ "$c" = "Kernel" ]; then
echo $b
fi
done)
# The Kernel partition, starting at $offset, is divided into
# two areas at $boundary. We therefore need to split the
# kernel into two and write them to flash with two Sercomm
# headers.
boundary=1441792 # 0x00160000
ksize1=$(($boundary - $offset - 16))
printf "Flashing kernel: " >&2
{
sercomm_header $(($kfilesize + 16))
dd if="$kfile" of="$tmpdir/kpart1" bs=$ksize1 \
count=1 2>/dev/null
nslu2_swap "$tmpdir/kpart1"
rm -f "$tmpdir/kpart1"
sercomm_header 131072
dd if="$kfile" of="$tmpdir/kpart2" ibs=$ksize1 \
skip=1 2>/dev/null
nslu2_swap "$tmpdir/kpart2"
rm -f "$tmpdir/kpart2"
} > "$kmtd" || error "failed."
echo "done." >&2
printf "Flashing initramfs: " >&2
dd if="$ifile" of="$tmpdir/initrd" ibs=$(($imtdsize - 16)) \
conv=sync 2>/dev/null
{
sercomm_header $ifilesize
nslu2_swap "$tmpdir/initrd"
rm -f "$tmpdir/initrd"
} > "$imtd" || error "failed."
echo "done." >&2
;;
"olpc")
[ -n "$ifile" ] || error "Initrd required for olpc method"
local olpcfth="$(mktemp --tmpdir=$tmpdir)"
local olpc_boot_dir="$(findmnt -n --output target --target /boot)"
local kernel="$(realpath --relative-to=$olpc_boot_dir $kfile |sed 's!/!\\\\!')"
local initrd="$(realpath --relative-to=$olpc_boot_dir $ifile |sed 's!/!\\\\!')"
printf "Generating olpc.fth... " >&2
sed -e "s!@@KERNEL@@!$kernel!g" \
-e "s!@@INITRD@@!$initrd!g" \
-e "s!@@LINUX_KERNEL_CMDLINE@@!$(get_kernel_cmdline)!g" \
-e "s!@@LINUX_KERNEL_CMDLINE_DEFAULTS@@!$(get_kernel_cmdline_defaults)!g" \
< "$BOOTSCRIPTS_DIR/olpc.fth" > $olpcfth
mkdir -p "$olpc_boot_dir/boot"
echo "done." >&2
backup_and_install "$olpcfth" "$olpc_boot_dir/boot/olpc.fth"
;;
esac
}
# vim:noexpandtab shiftwidth=8 syntax=sh