521 lines
15 KiB
Bash
521 lines
15 KiB
Bash
#!/bin/sh
|
|
#===============================================================================
|
|
#
|
|
# recovery-initramfs-init
|
|
#
|
|
# Copyright (C) 2016, 2017 by Digi International Inc.
|
|
# All rights reserved.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify it
|
|
# under the terms of the GNU General Public License version 2 as published by
|
|
# the Free Software Foundation.
|
|
#
|
|
#
|
|
# !Description: Init script for recovery initramfs
|
|
#
|
|
#===============================================================================
|
|
|
|
# Variables.
|
|
#------------------------------------------------------------------------------
|
|
ENV_BOOT_RECOVERY="boot_recovery"
|
|
ENV_RECOVERY_COMMAND="recovery_command"
|
|
|
|
SW_CONFIG="/etc/swupdate.cfg"
|
|
PUBLIC_KEY="/etc/ssl/certs/key.pub"
|
|
|
|
USB_MOUNT_DIR="/run/media"
|
|
UPDATE_MOUNT_DIR="/mnt/update"
|
|
|
|
REBOOT_TIME=10
|
|
|
|
# Functions.
|
|
#------------------------------------------------------------------------------
|
|
#------------------------------------------------------------------------------
|
|
# Function - log
|
|
#
|
|
# Prints the given text in the console.
|
|
#
|
|
# @param ${1} - Text to print.
|
|
#------------------------------------------------------------------------------
|
|
log() {
|
|
echo "[RECOVERY] ${1}" 1>&2
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - log_warning
|
|
#
|
|
# Prints the given text in the console as a warning.
|
|
#
|
|
# @param ${1} - Warning text to print.
|
|
#------------------------------------------------------------------------------
|
|
log_warning() {
|
|
log "[WARNING] ${1}"
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - log_error
|
|
#
|
|
# Prints the given text in the console as an error.
|
|
#
|
|
# @param ${1} - Error text to print.
|
|
#------------------------------------------------------------------------------
|
|
log_error() {
|
|
log "[ERROR] ${1}"
|
|
psplash_message "ERROR: ${1}"
|
|
psplash_progress "0"
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - clear_uboot_vars
|
|
#
|
|
# Clears recovery U-Boot variables.
|
|
#------------------------------------------------------------------------------
|
|
clear_uboot_vars() {
|
|
fw_setenv "${ENV_BOOT_RECOVERY}"
|
|
fw_setenv "${ENV_RECOVERY_COMMAND}"
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - read_uboot_var
|
|
#
|
|
# Reads the given U-Boot variable.
|
|
#
|
|
# @param ${1} - U-Boot variable to read.
|
|
# @param ${2} - Where to store the value of the read variable.
|
|
#------------------------------------------------------------------------------
|
|
read_uboot_var() {
|
|
eval "${2}=\"$(fw_printenv -n ${1} 2>/dev/null)\""
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - set_uboot_var
|
|
#
|
|
# Sets the given U-Boot variable.
|
|
#
|
|
# @param ${1} - U-Boot variable to set.
|
|
# @param ${2} - Value to set.
|
|
#------------------------------------------------------------------------------
|
|
set_uboot_var() {
|
|
fw_setenv ${1} ${2} 2>/dev/null
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - psplash_message
|
|
#
|
|
# Shows the given message in the psplash screen.
|
|
#
|
|
# @param ${1} - Message to show.
|
|
#------------------------------------------------------------------------------
|
|
psplash_message() {
|
|
echo "MSG ${1}" > /tmp/psplash_fifo
|
|
sleep 0.2
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - psplash_progress
|
|
#
|
|
# Sets the psplash progress bar percentage to the given one.
|
|
#
|
|
# @param ${1} - Progress percentage.
|
|
#------------------------------------------------------------------------------
|
|
psplash_progress() {
|
|
echo "PROGRESS ${1}" > /tmp/psplash_fifo
|
|
sleep 0.2
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - reboot_system
|
|
#
|
|
# Reboots the system.
|
|
#------------------------------------------------------------------------------
|
|
reboot_system() {
|
|
sync && reboot -f
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - quit_with_error
|
|
#
|
|
# Ends the recovery process with the given error message.
|
|
#
|
|
# @param ${1} - Error message.
|
|
#------------------------------------------------------------------------------
|
|
quit_with_error() {
|
|
clear_uboot_vars
|
|
log_error "${1}"
|
|
log "The system will now reboot in ${REBOOT_TIME} seconds"
|
|
sleep "${REBOOT_TIME}"
|
|
reboot_system
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - is_nand
|
|
#
|
|
# Verifies if the system is running in a NAND flash.
|
|
#
|
|
# @return - "yes" if the system is running in NAND, "no" otherwise
|
|
#------------------------------------------------------------------------------
|
|
is_nand() {
|
|
if [ -f /proc/mtd ] && grep -qs mtd /proc/mtd; then
|
|
echo "yes"
|
|
else
|
|
echo "no"
|
|
fi
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - format_partition
|
|
#
|
|
# Formats the given partition.
|
|
#
|
|
# @param ${1} - Partition name to format.
|
|
#------------------------------------------------------------------------------
|
|
format_partition() {
|
|
if [ "$(is_nand)" = "yes" ]; then
|
|
format_ubi_volume "${1}"
|
|
else
|
|
format_emmc_block "${1}"
|
|
fi
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - format_ubi_volume
|
|
#
|
|
# Formats and re-creates the given UBI volume.
|
|
#
|
|
# @param ${1} - UBI Volume name to format.
|
|
#------------------------------------------------------------------------------
|
|
format_ubi_volume() {
|
|
# Find the MTD partition.
|
|
local mtd_num="$(sed -ne "s/mtd\([0-9]\+\):.*\<${1}\>.*/\1/g;T;p" /proc/mtd 2>/dev/null)"
|
|
if [ -z "${mtd_num}" ]; then
|
|
quit_with_error "Could not find MTD partition for volume '${1}'"
|
|
else
|
|
# Umount in case partition is mounted, ignore errors.
|
|
if grep -qs "${1}" /proc/mounts; then
|
|
local path="$(sed -ne "s/.*:${1} \(.*\) ubifs.*/\1/g;T;p" /proc/mounts 2>/dev/null)"
|
|
umount "${path}" >/dev/null 2>&1
|
|
fi
|
|
ubidetach -p "/dev/mtd${mtd_num}" >/dev/null 2>&1
|
|
# Format MTD partition.
|
|
if ! ubiformat "/dev/mtd${mtd_num}" -q -y; then
|
|
quit_with_error "Error erasing '/dev/mtd${mtd_num}' block"
|
|
fi
|
|
psplash_progress "50"
|
|
# Attach and get UBI device number
|
|
local dev_number="$(ubiattach -p /dev/mtd${mtd_num} 2>/dev/null | sed -ne 's,.*device number \([0-9]\).*,\1,g;T;p' 2>/dev/null)"
|
|
# Create UBI Vol.
|
|
ubimkvol "/dev/ubi${dev_number}" -m -N "${1}" >/dev/null 2>&1
|
|
if [ "$?" = "0" ]; then
|
|
# Configure the empty UBIFS partition to use ZLIB
|
|
[ "${1}" = "update" ] && UBIFS_COMPRESSION="-x zlib"
|
|
|
|
volid="$(ubinfo "/dev/ubi${dev_number}" -N "${1}" | sed -ne 's,Volume ID:[[:blank:]]\+\([0-9]\+\)[[:blank:]]\+.*,\1,g;T;p')"
|
|
mkfs.ubifs ${UBIFS_COMPRESSION} -F /dev/ubi${dev_number}_${volid}
|
|
psplash_progress "100"
|
|
log "Partition '${1}' successfully erased!"
|
|
# Detach MTD partition.
|
|
ubidetach -p "/dev/mtd${mtd_num}" >/dev/null 2>&1
|
|
else
|
|
quit_with_error "Error creating '${1}' UBI volume"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - format_emmc_block
|
|
#
|
|
# Formats the given emmc partition block name.
|
|
#
|
|
# @param ${1} - Partition name to format.
|
|
#------------------------------------------------------------------------------
|
|
format_emmc_block() {
|
|
# Find partition block number.
|
|
local partition_block="/dev/mmcblk0p$(fdisk -l /dev/mmcblk0 | sed -ne "s,^[^0-9]*\([0-9]\+\).*\<${1}\>.*,\1,g;T;p")"
|
|
if [ -b "${partition_block}" ]; then
|
|
# Umount in case partition is mounted, ignore errors.
|
|
if grep -qs "${partition_block}" /proc/mounts; then
|
|
umount "${partition_block}" >/dev/null 2>&1
|
|
fi
|
|
# Format emmc block.
|
|
mkfs.ext4 "${partition_block}" >/dev/null 2>&1
|
|
if [ "$?" = "0" ]; then
|
|
psplash_progress "100"
|
|
log "Partition '${1}' successfully erased!"
|
|
else
|
|
quit_with_error "Error erasing '${1}' partition"
|
|
fi
|
|
else
|
|
quit_with_error "Could not find partition block for '${1}'"
|
|
fi
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - swu_package_path
|
|
#
|
|
# Get absolute path of update package searching in local media
|
|
#
|
|
# @param ${1} - SWU package
|
|
#------------------------------------------------------------------------------
|
|
swu_package_path() {
|
|
# Check whether the package is local and get the absolute path.
|
|
if echo "${1}" | grep -qs '^file://'; then
|
|
local pkg_name="$(basename ${1})"
|
|
|
|
for i in ${UPDATE_MOUNT_DIR} $(echo ${USB_MOUNT_DIR}/*); do
|
|
echo $i | grep -qs "${USB_MOUNT_DIR}/\*" && continue
|
|
if [ -f "${i}/${pkg_name}" ]; then
|
|
swu_abspath="${i}/${pkg_name}"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ -n "${swu_abspath}" ]; then
|
|
echo "${swu_abspath}"
|
|
else
|
|
quit_with_error "Unable to find update package '${pkg_name}'"
|
|
fi
|
|
else
|
|
echo "${1}"
|
|
fi
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - check_swu_package
|
|
#
|
|
# Check if the update package is a valid one and get its absolute path
|
|
#
|
|
# @param ${1} - SWU package
|
|
#------------------------------------------------------------------------------
|
|
check_swu_package() {
|
|
log "Checking update package '$(basename ${1})'"
|
|
|
|
if [ -z "${1}" ]; then
|
|
quit_with_error "Firmware update package not specified"
|
|
fi
|
|
|
|
update_package="$(swu_package_path ${1})"
|
|
|
|
# Check software update package.
|
|
if [ -f "${PUBLIC_KEY}" ]; then
|
|
swupdate -c -i "${update_package}" -k "${PUBLIC_KEY}"
|
|
else
|
|
swupdate -c -i "${update_package}"
|
|
fi
|
|
|
|
if [ "$?" != "0" ]; then
|
|
quit_with_error "Invalid update package '$(basename ${1})'"
|
|
fi
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - set_encryption_flag
|
|
#
|
|
# Set the rootfs encryption flag to the mtdparts variable.
|
|
#------------------------------------------------------------------------------
|
|
set_encryption_flag() {
|
|
if [ "$(is_nand)" = "no" ]; then
|
|
return
|
|
fi
|
|
|
|
# Read the mtdparts variable.
|
|
read_uboot_var mtdparts mtdparts
|
|
|
|
# Check if there is any command.
|
|
if [ -z "${mtdparts}" ]; then
|
|
quit_with_error "No mtdparts found"
|
|
fi
|
|
|
|
# Parse the mtdparts value.
|
|
case "${mtdparts}" in
|
|
*\(rootfs\)enc*)
|
|
# Partition already flagged.
|
|
;;
|
|
*\(rootfs\)*)
|
|
# Add the flag to the rootfs.
|
|
local new_mtdparts=$(echo "${mtdparts}" | sed "s/(rootfs)/(rootfs)enc/g")
|
|
set_uboot_var mtdparts "${new_mtdparts}"
|
|
;;
|
|
*)
|
|
quit_with_error "Error flagging rootfs as encrypted"
|
|
;;
|
|
esac
|
|
|
|
if [ -n "${update_package_bool}" ]; then
|
|
# Modify the recovery command not to set again the key.
|
|
new_command=$(echo "${COMMAND}" | sed "s/encryption_key=[^ ]*//g")
|
|
set_uboot_var "${ENV_RECOVERY_COMMAND}" "${new_command}"
|
|
psplash_progress "100"
|
|
reboot_system
|
|
fi
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Function - get_kernel_version
|
|
#
|
|
# Obtain the kernel version and return it in number format
|
|
#
|
|
# @return - kernel version
|
|
#------------------------------------------------------------------------------
|
|
get_kernel_version() {
|
|
major="$(uname -r | cut -d'.' -f1)"
|
|
minor="$(uname -r | cut -d'.' -f2)"
|
|
echo $(((major << 8) + minor))
|
|
}
|
|
|
|
# Main
|
|
#------------------------------------------------------------------------------
|
|
# Setup the environment.
|
|
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
|
|
|
|
# Mount virtual file system.
|
|
mkdir -p /proc /sys /dev /tmp
|
|
mount -t proc proc /proc
|
|
mount -t sysfs sysfs /sys
|
|
mount -t devtmpfs devtmpfs /dev
|
|
mount -t tmpfs tmpfs /tmp
|
|
|
|
# Register mdev as device manager
|
|
if [ -f /proc/sys/kernel/hotplug ]; then
|
|
echo > /dev/mdev.seq
|
|
echo > /dev/mdev.log
|
|
echo "/sbin/mdev" > /proc/sys/kernel/hotplug
|
|
mdev -s
|
|
fi
|
|
|
|
# Give some time for the devices to settle down
|
|
sleep 2
|
|
|
|
# Run all shell scripts in postinstall folder
|
|
run-parts /etc/*-postinsts
|
|
|
|
# Setup fw_printenv.
|
|
mkdir -p /var/lock
|
|
|
|
# Set kernel console loglevel.
|
|
sysctl -q -w kernel.printk=4
|
|
|
|
# Launch 'rngd' to feed random data to kernel entropy pool
|
|
# for kernels previous to v3.17
|
|
if [ "$(get_kernel_version)" -lt "$(((3 << 8) + 17))" ]; then
|
|
mkdir -p /var/run && rngd
|
|
fi
|
|
|
|
# Start psplash.
|
|
psplash &
|
|
|
|
# Parse the kernel command line.
|
|
for arg in $(cat /proc/cmdline); do
|
|
case "${arg}" in
|
|
rescue=1) eval "${arg}";;
|
|
esac
|
|
done
|
|
|
|
# Jump to a rescue shell if requested.
|
|
if [ -n "${rescue}" ]; then
|
|
# Expand console and respawn if exited
|
|
while true; do
|
|
setsid cttyhack sh -l
|
|
sleep 1
|
|
done
|
|
fi
|
|
|
|
log "Starting recovery..."
|
|
psplash_message "Starting recovery..."
|
|
|
|
# Read the recovery command.
|
|
read_uboot_var "${ENV_RECOVERY_COMMAND}" COMMAND
|
|
|
|
# Check if there is any command.
|
|
if [ -z "${COMMAND}" ]; then
|
|
quit_with_error "No command found"
|
|
fi
|
|
|
|
# Parse the recovery command.
|
|
for arg in ${COMMAND}; do
|
|
case "${arg}" in
|
|
wipe_update)
|
|
wipe_update_bool=true;;
|
|
encryption_key=*)
|
|
encryption_key_bool=true;
|
|
eval "${arg}";;
|
|
update_package=*)
|
|
update_package_bool=true;
|
|
eval "${arg}";;
|
|
*)
|
|
# Not supported command
|
|
quit_with_error "Unknown recovery command '${arg}'";;
|
|
esac
|
|
done
|
|
|
|
# Sanity checks.
|
|
if [ -n "${update_package_bool}" ]; then
|
|
check_swu_package "${update_package}"
|
|
fi
|
|
|
|
# Check if encryption key command is configured.
|
|
if [ -n "${encryption_key_bool}" ]; then
|
|
log "Trustfence encryption key setup requested (new key: ${encryption_key:-random})"
|
|
psplash_message "Configuring new encryption key..."
|
|
psplash_progress "0"
|
|
|
|
trustfence-tool "--newkey${encryption_key:+=${encryption_key}}"
|
|
if [ "$?" = "0" ]; then
|
|
psplash_progress "10"
|
|
log "Trustfence encryption key setup succeed!"
|
|
else
|
|
quit_with_error "Error configuring trustfence encryption key"
|
|
fi
|
|
|
|
# Set the encryption flag to the rootfs.
|
|
set_encryption_flag
|
|
|
|
# Format partition.
|
|
if [ "$(is_nand)" = "no" ]; then
|
|
psplash_message "Formatting rootfs partition..."
|
|
rootfs_block="/dev/mmcblk0p$(fdisk -l /dev/mmcblk0 | sed -ne "s,^[^0-9]*\([0-9]\+\).*\<rootfs\>.*,\1,g;T;p")"
|
|
trustfence-tool --format ${rootfs_block} cryptroot
|
|
fi
|
|
psplash_progress "100"
|
|
fi
|
|
|
|
# Check if update package command is configured.
|
|
if [ -n "${update_package_bool}" ]; then
|
|
log "Firmware update requested"
|
|
psplash_message "Updating firmware..."
|
|
psplash_progress "0"
|
|
|
|
# Format the UBI volume before updating.
|
|
if [ "$(is_nand)" = "yes" ]; then
|
|
psplash_message "Formatting rootfs partition..."
|
|
format_ubi_volume rootfs
|
|
psplash_progress "0"
|
|
fi
|
|
|
|
log "Update package location: ${update_package}"
|
|
# Execute the progress binary.
|
|
progress -wp &
|
|
# Execute the software update.
|
|
if [ -f "${PUBLIC_KEY}" ]; then
|
|
swupdate -f "${SW_CONFIG}" -i "${update_package}" -k "${PUBLIC_KEY}"
|
|
else
|
|
swupdate -f "${SW_CONFIG}" -i "${update_package}"
|
|
fi
|
|
if [ "$?" = "0" ]; then
|
|
log "Firmware update process succeed!"
|
|
else
|
|
quit_with_error "Error executing the firmware update"
|
|
fi
|
|
fi
|
|
|
|
# Check if wipe update patition command is configured.
|
|
if [ -n "${wipe_update_bool}" ]; then
|
|
log "Wipe 'update' partition requested"
|
|
psplash_message "Erasing update partition..."
|
|
psplash_progress "0"
|
|
format_partition update
|
|
fi
|
|
|
|
# End the recovery process.
|
|
clear_uboot_vars
|
|
reboot_system
|