xref: /aosp_15_r20/external/gsc-utils/util/re-sign-gsc.sh (revision 4f2df630800bdcf1d4f0decf95d8a1cb87344f5f)
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