1#!/bin/bash 2# Copyright 2023 The ChromiumOS Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6# This script allows to quickly re-sign a GSC image to a different set of 7# Device IDs. The script works with both Cr50 and Ti50, it must be run in the 8# appropriate top level directory, and the respective GSC binary has to be 9# present in the respective ./build directory. 10# 11# Two command line parameters are required, the device IDs of the GSC. 12# 13# The generated binary is saved in {cr,ti}50.<sha>.<devid0>.<devid1>.bin where 14# <sha> is the git sha of the current source tree. The suffix '-dirty' is 15# added to the <sha> component in case there are changes in any of the files 16# under git control. 17 18set -ue 19 20SCRIPT_NAME="$(basename "$0")" 21TMPD="$(mktemp -d "/tmp/${SCRIPT_NAME}.XXXXX")" 22NOCLEAN="${NOCLEAN:-}" 23if [[ -z ${NOCLEAN} ]]; then 24 trap 'rm -rf "${TMPD}"' EXIT 25fi 26 27# Make sure there is a codesigner in the path. 28CODESIGNER="" 29for f in cr50-codesigner \ 30 ../../cr50-utils/software/tools/codesigner/codesigner \ 31 codesigner; do 32 if command -v "${f}" > /dev/null 2>&1; then 33 CODESIGNER="${f}" 34 break 35 fi 36done 37if [[ -z ${CODESIGNER} ]]; then 38 echo "SCRIPT_NAME error: can't find codesigner" >&2 39 exit 1 40fi 41 42# Re-sign a single RW section. 43re_sign_rw() { 44 local tmp_file="$1" 45 local flash_base="$2" 46 local rw_base="$3" 47 local codesigner_params 48 local rw_size 49 local rw_bin_base 50 local rw_bin_size 51 local skip 52 53 codesigner_params=() 54 55 # Retrieve the rest of the codesigner command line arguments, which are this 56 # function's arguments after the three fixed ones. 57 shift 3 58 while [[ $# != 0 ]]; do 59 codesigner_params+=( "$1" ) 60 shift 61 done 62 63 # Determine RW size. It is 4 bytes at offset 808 into the signed header. 64 skip=$(( rw_base + 808 )) 65 rw_size="$(hexdump -s "${skip}" -n4 -e '1/4 "%d"' "${tmp_file}")" 66 67 # Extract the RW section to re-sign, dropping the existing header. 68 rw_bin_base=$(( rw_base + 1024 )) 69 rw_bin_size=$(( rw_size - 1024 )) 70 dd if="${full_bin}" of="${TMPD}/rw.bin" skip="${rw_bin_base}" bs=1 \ 71 count="${rw_bin_size}" status="none" 72 73 # Convert it to hex for signing 74 objcopy -I binary -O ihex --change-addresses $(( rw_bin_base + flash_base )) \ 75 "${TMPD}/rw.bin" "${TMPD}/rw.hex" 76 77 # Sign. 78 "${CODESIGNER}" "${codesigner_params[@]}" --input="${TMPD}/rw.hex" \ 79 --output="${TMPD}/rw.signed" 80 81 # Paste the result back into the original binary. 82 dd if="${TMPD}/rw.signed" of="${tmp_file}" seek="${rw_base}" conv="notrunc" \ 83 status="none" bs=1 84} 85 86main () { 87 local bin_size 88 local dev_id0 89 local dev_id1 90 local flash_base 91 local full_bin 92 local manifest 93 local output 94 local prefix 95 local rw_a_base 96 local rw_b_base 97 local rw_key 98 local sha 99 local tmp_file 100 local xml 101 102 if [[ $# -ne 2 ]]; then 103 echo "${SCRIPT_NAME} error:" >&2 104 echo " Two command line arguments are required, dev_id0 and dev_id1" >&2 105 exit 1 106 fi 107 108 dev_id0="$1" 109 dev_id1="$2" 110 111 full_bin="" 112 for f in build/ti50/dauntless/dauntless/full_image.signed.bin \ 113 build/cr50/ec.bin; do 114 if [[ -f ${f} ]]; then 115 full_bin="${f}" 116 break 117 fi 118 done 119 120 if [[ -z ${full_bin} ]]; then 121 echo "${SCRIPT_NAME} error: GSC binary not found" >&2 122 exit 1 123 fi 124 125 codesigner_params=( 126 --dev_id0="${dev_id0}" 127 --dev_id1="${dev_id1}" 128 --format=bin 129 --ihex 130 --no-icache 131 --override-keyid 132 --padbank 133 ) 134 135 bin_size="$(stat -c '%s' "${full_bin}")" 136 case "${bin_size}" in 137 (524288) rw_a_base=16384 # RO area size is fixed at 16K 138 rw_b_base=$(( bin_size / 2 + rw_a_base )) 139 rw_key="util/signer/cr50_rom0-dev-blsign.pem.pub" 140 manifest="util/signer/ec_RW-manifest-dev.json" 141 xml="util/signer/fuses.xml" 142 codesigner_params+=( --b ) 143 flash_base=262144 144 prefix="cr50" 145 ;; 146 (1048576) local rw_bases 147 # Third and sixths lines showing signed header magic are base 148 # addresses of RW_A and RW_B. 149 mapfile -t rw_bases < <(od -Ax -t x1 "${full_bin}" |awk ' 150 /^....00 fd ff ff ff/ { 151 line = line + 1; 152 if (line % 3 == 0) { 153 print strtonum("0x"$1) 154 } 155 }') 156 rw_a_base="${rw_bases[0]}" 157 rw_b_base="${rw_bases[1]}" 158 rw_key="ports/dauntless/signing/ti50_dev.key" 159 manifest="ports/dauntless/signing/manifest.TOT.json" 160 xml="ports/dauntless/signing/fuses.xml" 161 codesigner_params+=( --dauntless ) 162 flash_base=524288 163 prefix="ti50" 164 ;; 165 (*) echo "What is ${full_bin}?" >&2 166 exit 1 167 ;; 168 esac 169 170 # Determine the current git tree state. This would match the binary image's 171 # version string if it was built from this tree. 172 sha="$(git rev-parse --short HEAD)" 173 if git status --porcelain 2>&1 | grep -qv '^ *?'; then 174 sha="${sha}-dirty" 175 fi 176 177 # Check if the output file already exists. 178 output="$(printf "${prefix}.${sha}.%08x-%08x.bin" "${dev_id0}" "${dev_id1}")" 179 if [[ -f ${output} ]]; then 180 echo "${output} is already there" 181 exit 0 182 fi 183 184 # Make sure all necessary files are present. 185 for f in "${rw_key}" "${manifest}" "${xml}"; do 186 if [[ ! -f ${f} ]]; then 187 echo "File ${f} not found" >&2 188 exit 1 189 fi 190 done 191 192 # Clean up the manifest. 193 sed -zE 's/"board_[^,]+,\s*//g;s/"info"[^}]+},\s*/"info": { },/' \ 194 "${manifest}" > "${TMPD}/manifest.json" 195 196 tmp_file="${TMPD}/full.bin" 197 cp "${full_bin}" "${tmp_file}" 198 199 codesigner_params+=( 200 --json "${TMPD}/manifest.json" 201 --key "${rw_key}" 202 -x "${xml}" 203 ) 204 205 echo "Re-signing a ${prefix} image" 206 207 re_sign_rw "${tmp_file}" "${flash_base}" "${rw_a_base}" \ 208 "${codesigner_params[@]}" 209 re_sign_rw "${tmp_file}" "${flash_base}" "${rw_b_base}" \ 210 "${codesigner_params[@]}" 211 212 cp "${tmp_file}" "${output}" 213} 214 215main "$@" 216