blob: cc16feae0f0fb7a08910ae1aaee66bfcbeb002f2 [file] [log] [blame]
#!/bin/bash
# Copyright 2018 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 script provides user interface to the 'open box RMA' procedure which
# verifies integrity of the RO areas of the RMAed device's AP and EC firmware.
#
# The script is run on a secure Chrome OS device which is connected with SuzyQ
# cable to the RMAed device (also referred to as "device under test" a.k.a.
# DUT).
#
# The script checks the Cr50 version run on the DUT and updates it if
# necessary. After that this script invokes gsctool to perform actual RO
# verification.
GSCTOOL="/usr/sbin/gsctool"
# Remember pid of the top level script to be able to terminate it from
# subprocesses.
TOP_PID="$$"
trap "exit 1" TERM
# Terminal decorations for error and success messages.
V_BOLD_RED="$(tput bold; tput setaf 1)"
V_BOLD_GREEN="$(tput bold; tput setaf 2)"
V_VIDOFF="$(tput sgr0)"
die() {
local text="$*"
echo "${V_BOLD_RED}${text}${V_VIDOFF}" >&2
kill -s TERM "${TOP_PID}"
}
# Retrieve board ID and flags values from the H1 on the DUT.
#
# Output of 'gsctool -i' contains a line of the following format:
#
# Board ID space: <hex board ID>:~<hex board id>:<hex flags>
#
# The <hex board ID> value is included twice, straight and inverted.
#
# This function verifies that board ID value is valid and prints a string
# consisting of board ID and flags, each prepended with '0x'.
get_chip_bid_values() {
local flags
local xor
IFS=' ' flags=( $("${GSCTOOL}" -i |
awk -F':' '
/Board ID space: / {
sub(" ", "", $2);
print "0x"$2" 0x"$3" 0x"$4
}') )
if [[ "${#flags[@]}" != 3 ]]; then
die "Wrong format of chip flags: ${flags[*]}"
fi
# verify that board ID is intact
xor=$(( ( ${flags[0]} ^ ~${flags[1]} ) & 0xffffffff ))
if [[ ${xor} != 0 ]]; then
die "Invalid chip board ID value bid ${flags[0]}, ~bid ${flags[1]}"
fi
# Echo Board ID and flags
echo "${flags[0]} ${flags[2]}"
}
# Retrieve RLZ from the H1 on the DUT.
#
# Output of 'gsctool -i -M' contains a line of the following format:
#
# BID_RLZ=<RLZ String>
#
get_chip_rlz_value() {
${GSCTOOL} -i -M | grep BID_RLZ | sed -e 's/BID_RLZ=//g;'
}
# Retrieve Cr50 binary's board ID and flags values.
#
# Ouput of 'gsctool -b cr50.bin' contains a line of the following format:
#
# RO_A:0.0.10 RW_A:0.0.24[<board ID>:<hex board ID mask>:<hex flags] ...
#
# For the purposes of this script RW_A and RW_B sections are guaranteed to
# have the same board ID programmed in their respective headers.
#
# The actual <board ID> value could be expressed as an 8 symbol hex or a 4
# symbol ASCII (the RLZ code). This function converts the ASCII into hex if
# necessary and prints out a string consisting of hex board ID, hex board ID
# mask and hex flags, all prepended with '0x'.
get_image_bid_values() {
local image="${1}"
local flags
IFS=':' flags=( $("${GSCTOOL}" -b "${image}" |
awk '{
if (match($0, /\[[^\]]+\]/)) {
print substr($0, RSTART + 1, RLENGTH - 2)
}
}') )
if [[ "${#flags[@]}" != 3 ]]; then
die "Wrong format of image flags: ${flags[*]}"
fi
if [[ "${#flags[0]}" == 4 ]]; then
# Convert ASCII board name into hex.
flags[0]="$(printf "${flags[0]}" | hexdump -v -e '/1 "%02X"')"
fi
echo "${flags[@]/#/0x}"
}
# Update DUT Cr50 firmware to the passed in image.
update_dut() {
local chip_bid
local chip_flags
local chip_values
local image_bid
local image_file
local image_flags
local image_mask
local image_values
image_file="$1"
# Retreive board ID header fields of the image file.
IFS=' ' image_values=( $(get_image_bid_values "${image_file}" ) )
image_bid="${image_values[0]}"
image_mask="${image_values[1]}"
image_flags="${image_values[2]}"
# Retrieve board ID fields of the H1 on the DUT.
chip_values=( $(get_chip_bid_values) )
chip_bid="${chip_values[0]}"
chip_flags="${chip_values[1]}"
# Verify that board ID of the image is suitable for the chip.
match=$(( (image_bid & image_mask) == (chip_bid & image_mask) ))
if [[ ${match} != 1 ]]; then
die "Image board ID ${image_bid} and mask ${image_mask} " \
"incompatible with chip board ID ${chip_bid}"
fi
# Verify that flags of the image are compatible with the chip.
match=$(( (image_flags & chip_flags) == image_flags ))
if [[ ${match} != 1 ]]; then
die "Image flags ${image_flags} incompatible with chip flags ${chip_flags}"
fi
${GSCTOOL} "${image_file}"
}
# Convert string version representation into ordinal number.
#
# String version representation is of the form
#
# <epoch>.<major>.<minor>
#
# Where each field is a number. This function verifies the format and prints a
# single number which is calculated as
#
# (epoch * 16 + major) * 16 + minor
version_to_ord() {
local version="$1"
local split_version
local scale=256
if ! echo "${version}" | grep -qE "^([0-9]+\.){2}[0-9]+" ; then
die "Wrong version string format: ${version}"
fi
IFS='.' split_version=( ${version} )
echo "$(( (split_version[0] * scale + split_version[1]) * scale
+ split_version[2] ))"
}
# Compare two version strings and return 0 (i.e. 'success' in bash terms) if
# the first version expressed as an integer is lower than the second one.
update_needed() {
local dut_version="$1"
local image_version="$2"
local dut_ord
local image_ord
dut_ord="$(version_to_ord "${dut_version}")"
image_ord="$(version_to_ord "${image_version}")"
return $(( dut_ord >= image_ord ))
}
# The two arguments are the Cr50 image file of the lowest
# version DUT should be running, and the RO verification
# descriptors database file.
main() {
local dut_rw_version
local image_rw_version
local new_image
local ro_descriptions
local rlz
new_image="$1"
ro_descriptions="$2"
if [[ "$#" != 2 || ! -f "${new_image}" || ! -d "${ro_descriptions}" ]]; then
die "Two parameters are required: name of the Cr50 image file"\
"and name of the RO verification descriptors database file"
fi
# Retrieve Cr50 version running on the DUT. Reported in 'gsctool -f' output
# as RW <epoch>.<major>.<minor>
dut_rw_version="$(${GSCTOOL} -f | awk '/^RW / {print $2}')"
if [[ -z "${dut_rw_version}" ]]; then
die "Failed to retrieve DUT Cr50 version. Is DUT connected?"\
"You may need to flip suzy-q cable if DUT is already attached."
fi
# Retrieve RW Cr50 version of the supplied image file. Reported in 'gsctool
# -b' output as
#
# RO_A:<epoch>.<major>.<minor> RW_A:<epoch>.<major>.<minor>:[...
#
# RW_A and RW_B versions are expected to match for the purposes of this
# script.
image_rw_version="$(${GSCTOOL} -b "${new_image}" | awk '
/^RO_A/ {
match($2, /:.*\[/);
print substr($2, RSTART + 1, RLENGTH - 2)
}')"
# Check is the image running on the DUT is older than the supplied image, and
# if so - update the DUT.
if update_needed "${dut_rw_version}" "${image_rw_version}"; then
echo "Updating dut from ${dut_rw_version} to ${image_rw_version}"
update_dut "${new_image}"
echo "Waiting for the DUT to restart"
sleep 5 # Let it reboot.
echo "Verifying that update succeeded"
dut_rw_version="$(${GSCTOOL} -f | awk '/^RW / {print $2}')"
if update_needed "${dut_rw_version}" "${image_rw_version}"; then
die "Failed to update DUT to version ${image_rw_version}"
fi
fi
# Retrieve board RLZ
rlz=( $(get_chip_rlz_value) )
# Run RO verification and report results.
if ${GSCTOOL} -O "${ro_descriptions}/verify_ro_${rlz}.db"; then
echo "${V_BOLD_GREEN}Hash verification succeeded${V_VIDOFF}"
exit 0
else
echo "${V_BOLD_RED}Hash verification failed${V_VIDOFF}"
exit 1
fi
}
main "$@"