blob: 0f0e581cc700d2e56b6be1064cc95284f5dcb329 [file] [log] [blame]
#!/bin/sh
# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Version information. Please keep this in head of the updater.
TARGET_FWID="REPLACE_FWID"
TARGET_ECID="REPLACE_ECID"
TARGET_PDID="REPLACE_PDID"
TARGET_PLATFORM="REPLACE_PLATFORM"
TARGET_SCRIPT="REPLACE_SCRIPT"
STABLE_FWID="REPLACE_STABLE_FWID"
STABLE_ECID="REPLACE_STABLE_ECID"
STABLE_PDID="REPLACE_STABLE_PDID"
# Export all version information
export TARGET_FWID TARGET_ECID TARGET_PDID TARGET_PLATFORM TARGET_SCRIPT
export STABLE_FWID STABLE_ECID STABLE_PDID
set -e
# Global variables
SELF="$(readlink -f "$0")"
# Set by make_temp function and removed by clean_temp
TMP_DIR=
# Decides if we need to print debug messages
IS_DEBUG=
# The default script to be invoked from extracted bundle
SCRIPT="./${TARGET_SCRIPT:-updater.sh}"
# Tag file to prohibit updater execution
TAG_FILE_DISABLED='/root/.leave_firmware_alone'
# Set to True to bypass checking TAG_FILE_DISABLED
IS_FORCED=
# Set to true to prevent printing error alerts by error return value
IS_IGNORE_RC=
# Prints a message and return an error code ($1).
die_as() {
local ret="$1"
shift
echo "ERROR: $@" >&2
exit "$ret"
}
# Prints a message and return error as 1.
die() {
die_as 1 "$@"
}
# Prints messages if $IS_DEBUG is not empty.
debug() {
[ -z "$IS_DEBUG" ] || echo "$@" >&2
}
# Creates a temporary folder
make_temp() {
TMP_DIR="$(mktemp -d --tmpdir)" ||
die "Failed to create temporary folder"
trap clean_temp EXIT
}
# Creates a temporary folder with execution permission
make_exec_temp() {
make_temp
debug "bind and remount for allowing execution ..."
mount --bind "$TMP_DIR" "$TMP_DIR" &&
mount -o remount,exec "$TMP_DIR" ||
die "Failed to enable execution permission of folder $TMP_DIR"
}
# Cleans temporary folders
clean_temp() {
debug "clean_temp: started."
if [ -d "$TMP_DIR" ]; then
umount -f "$TMP_DIR" >/dev/null 2>&1 || true
rm -rf "$TMP_DIR" >/dev/null 2>&1 || true
TMP_DIR=""
fi
}
# Extracts bundle content to specified location
extract_bundle() {
local destination="$1"
sh "$SELF" --sb_extract "$destination" >/dev/null ||
die "Cannot extract bundle content to: $destination"
}
# Executes a script in bundle
exec_bundle_script() {
local rc=0
make_exec_temp
extract_bundle "$TMP_DIR"
[ -x "$TMP_DIR/$SCRIPT" ] || die "Missing program in bundle: $SCRIPT"
debug "Start running script: $SCRIPT $@"
(cd "$TMP_DIR" && "$SCRIPT" "$@") || rc="$?"
if [ "$rc" -ne 0 -a -z "$IS_IGNORE_RC" ]; then
die_as "$rc" "Execution failed: $SCRIPT (error code = $rc)"
fi
exit "$rc"
}
# Prepares for extraction with shar
prepare_shar_extract() {
local destination="$1"
if [ -z "$destination" ]; then
make_temp
destination="$TMP_DIR"
# Don't remove the temporary files
TMP_DIR=""
fi
echo "Extracting to: $destination"
cd "$destination" || die "Invalid destination: $destination"
exec >/dev/null # Prevent shar messages in stdout
set -- "-c" # Force shar to overwrite files
}
find_version_string() {
local filename="$1"
local pattern="$2"
if [ ! -s "$filename" ]; then
return
fi
# Chrome OS & chroot has dump_fmap, and standard Linux desktop has strings.
if type dump_fmap >/dev/null 2>&1; then
local tmpdir="$(mktemp -d)"
local filepath="$(readlink -f "$filename")"
(cd "$tmpdir"; dump_fmap -x "$filepath") >/dev/null 2>&1
cat "$tmpdir/RO_FRID" 2>/dev/null
rm -rf "$tmpdir"
elif type strings >/dev/null 2>&1; then
local version="$( (strings "$filename" | grep "$pattern" | uniq) || true)"
if [ "$(echo "version" | wc -l)" -ne "1" ]; then
version=""
fi
echo "$version"
else
(echo "WARNING: 'strings' and 'dump_fmap' are both not available."
echo " TARGET_{FW,EC,PD}ID can't be updated."
echo " You have to manually change that or repack on a desktop."
) >&2
fi
}
# Repacks current file ($SELF) by given source folder.
perform_shar_repack() {
local new_source="$1"
local cut_mark="$(sed -n '/^##CUTHERE##/=' "$SELF")"
local md5_file="$new_source/VERSION.md5"
local fw_ver="$(find_version_string "$new_source/bios.bin" '^Google_')"
local ec_ver="$(find_version_string "$new_source/ec.bin" \
'^[a-zA-Z0-9]*_v[0-9\.]*-[a-z0-9]*$')"
local pd_ver="$(find_version_string "$new_source/pd.bin" \
'^[a-zA-Z0-9]*_v[0-9\.]*-[a-z0-9]*$')"
# Since mosys r430, trailing spaces reported by mosys is always scrubbed.
ec_ver="$(echo "$ec_ver" | sed 's/ *$//')"
[ "$cut_mark" -gt 0 ] || die "File corrupted: $SELF"
sed -i "$((cut_mark + 1)),\$d" "$SELF" ||
die "Failed to truncate existing data in $SELF"
# Try to update firmware version if available.
if [ -n "$fw_ver" ]; then
sed -i 's/^TARGET_FWID=".*"/TARGET_FWID="'"$fw_ver"'"/' "$SELF" &&
echo "Changed TARGET_FWID to $fw_ver"
[ -s "$new_source/VERSION" ] &&
sed -i "s/^BIOS version: .*/BIOS version: $fw_ver/" "$new_source/VERSION"
fi
if [ -n "$ec_ver" ]; then
sed -i 's/^TARGET_ECID=".*"/TARGET_ECID="'"$ec_ver"'"/' "$SELF" &&
echo "Changed TARGET_ECID to $ec_ver"
[ -s "$new_source/VERSION" ] &&
sed -i "s/^EC version: .*/EC version: $ec_ver/" "$new_source/VERSION"
fi
if [ -n "$pd_ver" ]; then
sed -i 's/^TARGET_PDID=".*"/TARGET_PDID="'"$pd_ver"'"/' "$SELF" &&
echo "Changed TARGET_PDID to $pd_ver"
[ -s "$new_source/VERSION" ] &&
sed -i "s/^PD version: .*/PD version: $pd_ver/" "$new_source/VERSION"
fi
# Update checksum data for every files except VERSION*
(cd "$new_source" &&
echo "Package Content:" &&
find . -type f '!' -name VERSION.md5 '!' -name VERSION \
-exec md5sum -b '{}' '+' ) >"$md5_file"
# Build shar content
(cd "$new_source" &&
shar -Q -q -x -m --no-character-count -D --no-i18n -z -g 1 . |
sed -r 's/^lock_dir=_sh.*/lock_dir=_fwupdate/;
s"^begin ([0-9]+) _sh[^/]*"begin \1 _fwupdate"
/^# Made on .* by/d;
/^# Source directory was /d') >>"$SELF" ||
die "Failed repacking from $new_source"
}
# Prints the VAR from '--param VAR' and '--param=VAR' format.
get_parameter_variable() {
local param="$1"
local param_name="${param%%=*}"
local param_value="${param#*=}"
if [ "$param" = "$param_name" ]; then
echo "$2"
else
echo "$param_value"
fi
}
# Main entry
main() {
local original_params="$*"
case "$1" in
--sb_extract | --sb_extract=*)
local destination="$(get_parameter_variable "$@")"
prepare_shar_extract "$destination"
return # Let shar handle the remaining stuff
;;
--sb_repack )
local new_source="$(get_parameter_variable "$@")"
[ -d "$new_source" ] || die "Invalid source folder: $new_source"
echo "Repacking from: $new_source"
perform_shar_repack "$new_source"
exit 0
;;
-V)
# Read information
make_temp
extract_bundle "$TMP_DIR"
cat "$TMP_DIR/VERSION"*
exit 0
;;
-h | "-?" | --help)
echo "
USAGE: $SELF [bundle_option|--] [updater_options]
bundle_option (only one option can be selected):
-h,--help: Show usage help
-V: show version and content of bundle
--force: force execution and ignore $TAG_FILE_DISABLED
--sb_extract [PATH]: extract bundle content to a temporary folder
--sb_repack PATH: update bundle content from given folder
updater_options:
"
# Invoke script with -h for usage help
IS_IGNORE_RC=TRUE
exec_bundle_script "-h"
;;
--force)
# Pass this into updaters
IS_FORCED=TRUE
;;
--debug | --debug | -v)
# do not shift here because this needs to be passed into the script
IS_DEBUG=TRUE
;;
--)
shift
;;
esac
# Do nothing if the OS specifies that.
# TODO(hungte) move this flag to kernel command line, or updater bundle
# itself.
if [ -e "$TAG_FILE_DISABLED" ] && [ -z "$IS_FORCED" ]; then
echo "WARNING: $SELF is disabled by $TAG_FILE_DISABLED"
echo "To force execution, please prefix --force to your command:"
echo " sudo $SELF --force $original_params"
exit 0
fi
exec_bundle_script "$@"
}
main "$@"
# Below are for shar execution. Don't put any code below main.
##CUTHERE##################################################################