blob: e861771c75a918a7f4e6dc35a781748eabd072f3 [file] [log] [blame]
#!/bin/bash
# Copyright (c) 2011 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.
# This utility writes a firmware image to a tegra based board.
# Include common CrOS utilities.
. "/usr/lib/crosutils/common.sh"
get_default_board
# Command line options
DEFINE_string board "${DEFAULT_BOARD}" "The name of the board to flash."
DEFINE_string variant "" "Board variant."
DEFINE_string firmware "" "Path to firmware image to write."
DEFINE_string flasher "" "Path to flasher image to use."
DEFINE_string script "" "Path to flasher script to use."
DEFINE_string bct "" "Path to bct image to write."
DEFINE_string dtb "" "Path to dtb (device tree blob) file to use."
DEFINE_string map "" "Path to System.map file to read for load address."
DEFINE_string config "" "Directory for temporary configuration files."
DEFINE_boolean sign $FLAGS_FALSE "Sign and append a BCT to the firmware image."
DEFINE_boolean dry_run $FLAGS_FALSE "Do everything except calling nvflash."
# Parse command line.
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"
# Die on errors.
set -e
CROS_LOG_PREFIX="cros_write_firmware"
# No arguments are expected; error if any are passed.
if [ $# -ne 0 ]; then
error "Unexpted arguments: $*"
exit 1
fi
if [ -z "${FLAGS_board}" ]; then
error "--board required."
exit 1
fi
get_board_and_variant $FLAGS_board $FLAGS_variant
# Display the U-Boot version string.
function show_u_boot_info() {
local firmware=$1
local header=$(strings "${firmware}" | grep "U-Boot" | head -1 )
info "Using ${header}"
}
# Given two values in decimal format return the first value rounded up to a
# multiple of the second value. The second value must be a power of two.
function round() {
awk "BEGIN { print and(($1 + $2 - 1), compl($2 - 1)) }"
}
# Given a value in decimal format return that value converted to hexidecimal
# and formatted as 0xXXXXXXXX.
function to_hex() {
awk "BEGIN { printf \"0x%x\", $1 }"
}
# Given flasher and dtb, return an estimated size that should be sufficiently
# large for the final flasher image (which is the two combined)
function estimate_final_flasher_size() {
local flasher_size=$(stat -c"%s" "$1")
local dtb_size=$(stat -c"%s" "$2")
round $((${flasher_size} + ${dtb_size})) 65536
}
# Create the flashing script. This script will be loaded and run by the flasher
# build of U-Boot. Most of the script is constant, we just need to append the
# sizes and address of the payload.
function make_flashing_script() {
local firmware=$1
local script=$2
local firmware_size=$(stat -c"%s" "${firmware}")
local image_address=$((${text_base} + ${script_offset} + ${script_size}))
echo "setenv address $(to_hex ${image_address})"
echo "setenv firmware_size $(to_hex ${firmware_size})"
echo "setenv length $(to_hex $(round ${firmware_size} 4096))"
cat "${script}"
}
# Parse the System.map file generated by the U-Boot build to get the entry
# point address.
function get_text_base() {
local map=$1
grep -m1 -E "^[0-9a-fA-F]{8} T _start$" ${map} | cut -d " " -f 1
}
###############################################################################
# Generate default values for the firmware, flasher, BCT, map and flash config
# file paths from the board and variant information provided.
firmware=$(find_u_boot_component "image.bin" "${FLAGS_firmware}")
flasher=$(find_u_boot_component "u-boot.bin" "${FLAGS_flasher}")
script=$(find_u_boot_component "flasher.script" "${FLAGS_script}")
bct=$(find_u_boot_component "bct/board.bct" "${FLAGS_bct}")
dtb=$(find_u_boot_component "dtb/${FLAGS_board/_/-}.dtb" "${FLAGS_dtb}")
map=$(find_u_boot_component "System.map" "${FLAGS_map}")
# Default value that hard-coded in u-boot-flasher.bin; use $(()) to convert
# hexadecimal into decimal values
script_offset=$((0x40000))
script_size=$((0x1000))
# Extract the load and entry point for U-Boot.
text_base="0x$(get_text_base "${map}")"
# Verify that all of the files and tools we will need are present.
check_for_file "firmware" " " "${firmware}"
check_for_file "flasher" " " "${flasher}"
check_for_file "script" " " "${script}"
check_for_file "BCT" " " "${bct}"
check_for_file "map" " " "${map}"
check_for_file "DTB" " " "${dtb}"
check_for_tool "nvflash" "nvflash"
check_for_tool "dtget" "dtc"
check_for_tool "dtput" "dtc"
show_u_boot_info "${firmware}"
# Estimate final script offset and size. This is only an estimation because the
# dtb size may (or may not) be increased after we write bootcmd into it. So
# later we will have to check whether the flasher size is not larger than our
# estimation.
script_offset="$(estimate_final_flasher_size "${flasher}" "${dtb}")"
script_size=$(round $(stat -c"%s" ${script}) 4096)
info "Flasher script offset: $(to_hex ${script_offset})"
info "Flasher script size: $(to_hex ${script_size})"
# If the user has not specified a config directory then create one on the fly
# and remove it when we are done.
if [ -z "${FLAGS_config}" ]; then
config=$(mktemp -d)
trap "rm -rf ${config}" EXIT
else
mkdir -p "${FLAGS_config}"
config=${FLAGS_config}
fi
# Optionally sign the firmware. This is usefull if you want to write a bare
# U-Boot to flash and boot with that instead of a fully build ChromiumOS
# firmware image.
if [ $FLAGS_sign -eq $FLAGS_TRUE ]; then
signed_firmware=${config}/signed.bin
cros_sign_bootstub \
--bct "${bct}" \
--bootstub "${firmware}" \
--output "${signed_firmware}" \
--config "${config}" \
--text_base "${text_base}"
else
signed_firmware=${firmware}
fi
# make a copy of dtb
cp ${dtb} ${config}/u-boot.dtb
dtb="${config}/u-boot.dtb"
# write bootcmd to the dtb
#
# FIXME "%[^^]" (matching anything except '^' character) is a
# hack of putting a bootcmd string that:
# * contains whitespace character
# * but does not contain '^' character (so the choice of '^'
# is somewhat arbitrarily)
# input the dtb file with a '\0' termination.
#
# We cannot use "%s", which stops at whitespace, and we cannot
# use "%${#bootcmd}c", which does not add '\0' to the scanned
# string. So our only option left is "%[^^]", which does not
# stop at whitespace and adds '\0' to the end.
#
bootcmd="source $(to_hex $((${text_base} + ${script_offset})))"
dtput -t s -f "%[^^]" ${dtb} /config/bootcmd "${bootcmd}"
info "Flasher bootcmd: '$(dtget -t s ${dtb} /config/bootcmd)'"
# create flasher
cat ${flasher} ${dtb} > ${config}/u-boot-flasher.bin
flasher="${config}/u-boot-flasher.bin"
# Create the flashing script and turn it into a U-Boot uImage.
make_flashing_script "${signed_firmware}" "${script}" > ${config}/script
mkimage -A arm -T script -d ${config}/script ${config}/script.uimg > /dev/null
# check whether our estimated script offset and size makes sense
final_script_offset=$(stat -c"%s" "${flasher}")
if [ ${final_script_offset} -gt ${script_offset} ]; then
die "script offset > estimation: $(to_hex ${final_script_offset})"
fi
final_script_size=$(stat -c"%s" "${config}/script.uimg")
if [ ${final_script_size} -gt ${script_size} ]; then
die "script size > estimation: $(to_hex ${final_script_size})"
fi
# Construct RAM image of flasher + script + firmware. The firmware should
# already be signed by cbootimage. The RAM image has a fixed 256K partition
# for the flasher, followed by the script and payload to be written.
b1=$((${script_offset} / 512))
b2=$(((${script_offset} + ${script_size}) / 512))
dd if="${flasher}" of="${config}/image" &> /dev/null
dd if="${config}/script.uimg" of="${config}/image" seek=$b1 &> /dev/null
dd if="${signed_firmware}" of="${config}/image" seek=$b2 &> /dev/null
if [ $FLAGS_dry_run -eq $FLAGS_FALSE ]; then
# Write the RAM image image to the board. The flasher will write the payload
# into the boot device using the flashing script.
sudo nvflash \
--bct "${bct}" \
--setbct \
--bl "${config}/image" \
--go \
--setentry "${text_base}" "${text_base}" ||
(echo && die "Failed to download U-Boot flasher + payload using nvflash.")
# TODO(robotboy): Make a U-Boot tool that can compute the same checksum on
# the host and display it here. Unfortunately U-Boot uses a slightly
# different crc32 than zlib, so it's not possible to use zlib's crc32 to do
# this. It has to be done by building a tool that uses U-Boots version.
info "The serial terminal or LCD panel should show before and after CRCs of"
info "the image written to the boot flash. If these values match each"
info "other your flash was probably successful."
fi