diff options
Diffstat (limited to 'util/signer/create_released_image.sh')
-rwxr-xr-x | util/signer/create_released_image.sh | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/util/signer/create_released_image.sh b/util/signer/create_released_image.sh new file mode 100755 index 0000000000..9dccd02aba --- /dev/null +++ b/util/signer/create_released_image.sh @@ -0,0 +1,222 @@ +#!/bin/bash + +# +# Copyright 2017 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 is a utility which allows to sign prod CR50 images for release +# and place them in a tarball suitable for uploading to the BCS. +# +# The util/signer/ec_RW-manifest-prod.json manifest present in the EC +# directory is used for signing. +# + +set -u + +# A very crude RO verification function. The key signature found at a fixed +# offset into the RO blob must match the RO type. Prod keys have bit D2 set to +# one, dev keys have this bit set to zero. +verify_ro() { + local ro_bin="${1}" + local type_expected="${2}" + local key_byte + + if [ ! -f "${ro_bin}" ]; then + echo "${ro_bin} not a file!" >&2 + exit 1 + fi + + # Key signature's lowest byte is byte #5 in the line at offset 0001a0. + key_byte="$(od -Ax -t x1 -v "${ro_bin}" | awk '/0001a0/ {print $6};')" + case "${key_byte}" in + (?[4567cdef]) + if [ "${type_expected}" == "prod" ]; then + return 0 + fi + ;; + (?[012389ab]) + if [ "${type_expected}" == "dev" ]; then + return 0 + fi + ;; + esac + + echo "RO key in ${ro_bin} does not match type ${type_expected}" >&2 + exit 1 +} + +# This function prepares a full CR50 image, consisting of two ROs and two RWs +# placed at their respective offsets into the resulting blob. It invokes the +# bs (binary signer) script to actually convert elf versions of RWs into +# binaries and sign them. +# +# The signed image is placed in the directory named as concatenation of RO and +# RW version numbers and board ID fields, if set to non-default. The ebuild +# downloading the tarball from the BCS expects the image to be in that +# directory. +prepare_image() { + local awk_prog + local count=0 + local extra_param= + local image_type="${1}" + local raw_version + local ro_a_hex="$(readlink -f "${2}")" + local ro_b_hex="$(readlink -f "${3}")" + local rw_a="$(readlink -f "${4}")" + local rw_b="$(readlink -f "${5}")" + local version + + for f in "${ro_a_hex}" "${ro_b_hex}"; do + if ! objcopy -I ihex "${f}" -O binary "${TMPD}/${count}.bin"; then + echo "failed to convert ${f} from hex to bin" >&2 + exit 1 + fi + verify_ro "${TMPD}/${count}.bin" "${image_type}" + : $(( count += 1 )) + done + + if [ "${image_type}" == "prod" ]; then + extra_param+=' prod' + fi + + if ! "${EC_ROOT}/util/signer/bs" ${extra_param} elves \ + "${rw_a}" "${rw_b}" > /dev/null; + then + echo "Failed invoking ${EC_ROOT}/util/signer/bs ${extra_param} " \ + "elves ${rw_a} ${rw_b}" >&2 + exit 1 + fi + + dd if="${TMPD}/0.bin" of="${RESULT_FILE}" conv=notrunc + dd if="${TMPD}/1.bin" of="${RESULT_FILE}" seek=262144 bs=1 conv=notrunc + + # A typical Cr50 version reported by gsctool looks as follows: + # RO_A:0.0.10 RW_A:0.0.22[ABCD:00000013:00000012] ...(the same for R[OW]_B). + # + # In case Board ID field is not set in the image, it is reported as + # [00000000:00000000:00000000] + # + # We want the generated tarball file name to include all relevant version + # fields. Let's retrieve the version string and process it using awk to + # generate the proper file name. Only the RO_A and RW_A version numbers are + # used, this script trusts the user to submit for processing a proper image + # where both ROs and both RWs are of the same version respectively. + # + # As a result, blob versions are converted as follows: + # RO_A:0.0.10 RW_A:0.0.22[ABCD:00000013:00000012] into + # r0.0.10.w0.0.22_ABCD_00000013_00000012 + # + # RO_A:0.0.10 RW_A:0.0.22[00000000:00000000:00000000] into + # r0.0.10.w0.0.22 + # + # The below awk program accomplishes this preprocessing. + awk_prog='/^RO_A:/ { + # drop the RO_A/RW_A strings + gsub(/R[OW]_A:/, "") + # Drop default mask value completely. + gsub(/\[00000000:00000000:00000000\]/, "") + # If there is a non-default mask: + # - replace opening brackets and colons with underscores. + gsub(/[\[\:]/, "_") + # - drop the trailing bracket. + gsub(/\]/, "") + # Print filtered out RO_A and RW_A values + print "r" $1 ".w" $2 +}' + + raw_version="$("${GSCTOOL}" -b "${RESULT_FILE}")" || + ( echo "${ME}: Failed to retrieve blob version" >&2 && exit 1 ) + + version="$(awk "${awk_prog}" <<< "${raw_version}" )" + if [ -z "${dest_dir}" ]; then + # Note that this is a global variable + dest_dir="cr50.${version}" + if [ ! -d "${dest_dir}" ]; then + mkdir "${dest_dir}" + else + echo "${dest_dir} already exists, will overwrite" >&2 + fi + elif [ "${dest_dir}" != "cr50.${version}" ]; then + echo "dev and prod versions mismatch!" >&2 + exit 1 + fi + + cp "${RESULT_FILE}" "${dest_dir}/cr50.bin.${image_type}" + echo "saved ${image_type} binary in ${dest_dir}/cr50.bin.${image_type}" +} + +# Execution starts here =========================== +ME="$(basename $0)" + +if [ -z "${CROS_WORKON_SRCROOT}" ]; then + echo "${ME}: This script must run inside Chrome OS chroot" >&2 + exit 1 +fi + +SCRIPT_ROOT="${CROS_WORKON_SRCROOT}/src/scripts" +. "${SCRIPT_ROOT}/build_library/build_common.sh" || exit 1 + +TMPD="$(mktemp -d /tmp/${ME}.XXXXX)" +trap "/bin/rm -rf ${TMPD}" SIGINT SIGTERM EXIT + +EC_ROOT="${CROS_WORKON_SRCROOT}/src/platform/ec" +RESULT_FILE="${TMPD}/release.bin" +dest_dir= +IMAGE_SIZE='524288' +export RESULT_FILE + +GSCTOOL="/usr/sbin/gsctool" +if [[ ! -x "${GSCTOOL}" ]]; then + emerge_command="USE=cr50_onboard sudo -E emerge ec-utils" + echo "${ME}: gsctool not found, run \"${emerge_command}\"" >&2 + exit 1 +fi + +DEFINE_string cr50_board_id "" \ + "Optional string representing Board ID field of the Cr50 RW header. +Consists of three fields separated by colon: <RLZ>:<hex mask>:<hex flags>" + +# Do not put this before the DEFINE_ invocations - they routinely experience +# error return values. +set -e + +FLAGS_HELP="usage: ${ME} [flags] <blobs> + +blobs are: + <prod RO A>.hex <prod RO B>.hex <RW.elf> <RW_B.elf>" + +# Parse command line. +FLAGS "$@" || exit 1 + +eval set -- "${FLAGS_ARGV}" +if [ "${#*}" != "4" ]; then + flags_help + exit 1 +fi + +dd if=/dev/zero bs="${IMAGE_SIZE}" count=1 2>/dev/null | + tr \\000 \\377 > "${RESULT_FILE}" +if [ "$(stat -c '%s' "${RESULT_FILE}")" != "${IMAGE_SIZE}" ]; then + echo "Failed creating ${RESULT_FILE}" >&2 + exit 1 +fi + +prod_ro_a="${1}" +prod_ro_b="${2}" +rw_a="${3}" +rw_b="${4}" + +# Used by the bs script. +export CR50_BOARD_ID="${FLAGS_cr50_board_id}" + +prepare_image 'prod' "${prod_ro_a}" "${prod_ro_b}" "${rw_a}" "${rw_b}" +tarball="${dest_dir}.tbz2" +tar jcf "${tarball}" "${dest_dir}" +rm -rf "${dest_dir}" + +bcs_path="gs://chromeos-localmirror/distfiles" +echo "SUCCESS!!!!!!" +echo "use the below commands to copy the new image to the BCS" +echo "gsutil cp ${tarball} ${bcs_path}" +echo "gsutil acl ch -u AllUsers:R ${bcs_path}/${tarball}" |