# Copyright (C) 2006 Joey Hess # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2016 Martin Michlmayr # Copyright (C) 2011 Loïc Minier # Copyright (C) 2011 Julian Andres Klode # Copyright (C) 2013-2016 Ian Campbell # 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" FK_ETC_MACHINE="${FK_ETC_MACHINE:-/etc/flash-kernel/machine}" 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:" ] && [ "$value" = "$machine" ]; then return 0 fi done return 1 } } get_boot_cmd() { case "$(dpkg --print-architecture)" in arm64|riscv64) echo booti ;; *) echo bootz ;; esac } 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 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/" } # 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:" ] && [ "$value" = "$machine" ]; 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 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 } get_kernel_cmdline() { . /etc/default/flash-kernel echo "$LINUX_KERNEL_CMDLINE" } get_kernel_cmdline_defaults() { . /etc/default/flash-kernel echo "$LINUX_KERNEL_CMDLINE_DEFAULTS" } mkimage_kernel() { local kaddr="$1" local epoint="$2" local kdesc="$3" local kdata="$4" local uimage="$5" printf "Generating kernel u-boot image... " >&2 mkimage -A "$mkarch" -O linux -T kernel -C none -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 "s!@@BOOT_CMD@@!$(get_boot_cmd)!g" \ -e "/@@UBOOT_ENV_EXTRA@@/{ s/@@UBOOT_ENV_EXTRA@@//g r $ubootenv }" < $sdata > $tdata \ -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 } # Return a nonempty string *unless* NO_CREATE_DOT_BAK_FILES is set. get_dot_bak_preference() { . /etc/default/flash-kernel 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() { . /etc/default/flash-kernel 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" } # 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 } # XXX needs testsuite coverage 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" } # XXX needs testsuite coverage android_flash() { local device="$1" printf "Flashing kernel and initramfs to $device... " >&2 abootimg -u "$device" -k "$kfile" -r "$ifile" >/dev/null || error "failed." echo "done." >&2 } find_dtb_file() { local dtb case "$dtb_dir" in /*) dtb="$dtb_dir/$dtb_name" if [ ! -f "$dtb" ]; then error "Couldn't find $dtb" fi ;; *) dtb=$(find /etc/flash-kernel/dtbs -name $dtb_name 2>/dev/null | head -n 1) if [ -z "$dtb" ]; then dtb=$(find /usr/lib/linux-image-$kvers -name $dtb_name 2>/dev/null | head -n 1) fi if [ ! -f "$dtb" ]; then error "Couldn't find DTB $dtb_name in /usr/lib/linux-image-$kvers or /etc/flash-kernel/dtbs" fi ;; esac echo $dtb } 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() { 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 [ "x$machine" = "xnone" ] && exit 0 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 latest_version=$(linux-version list | 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 if [ "$FK_KERNEL_HOOK_SCRIPT" = "postinst.d" ]; then exit 0 fi echo "Use --force if you want version $kvers." >&2 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 [ -f /etc/flash-kernel/ignore-efi ] && [ -z "$FK_IGNORE_EFI" ]; then FK_IGNORE_EFI="$(cat /etc/flash-kernel/ignore-efi)" fi if [ -d /sys/firmware/efi ] && [ "x$FK_IGNORE_EFI" != "xyes" ]; then # skipping when detect EFI echo "System running in EFI mode, skipping." exit 0 fi if ! check_supported "$machine"; then error "Unsupported platform '$machine'." fi kfile_suffix="" if ! kflavors=$(get_machine_field "$machine" "Kernel-Flavors") ; then # Since no Kernel-Flavors were specified, allow any kernel. kflavors="any" fi 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_multi_path="$(get_machine_field "$machine" "Boot-Multi-Path")" || : android_boot_device="$(get_machine_field "$machine" "Android-Boot-Device")" || : 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 "$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" 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 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_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 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" 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 ;; "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