1dcb3fd79SLei YU#!/bin/bash
2dcb3fd79SLei YUset -eo pipefail
3dcb3fd79SLei YU
4dcb3fd79SLei YUhelp=$'Generate Tarball with PNOR image and MANIFEST Script
5dcb3fd79SLei YU
6dcb3fd79SLei YUGenerates a PNOR SquashFS image from the PNOR image for VPNOR,
7dcb3fd79SLei YUOr use a static layout raw PNOR image,
8dcb3fd79SLei YUCreates a MANIFEST for image verification and recreation
9dcb3fd79SLei YUPackages the image and MANIFEST together in a tarball
10dcb3fd79SLei YU
11dcb3fd79SLei YUusage: generate-tar [OPTION] <PNOR FILE>...
12dcb3fd79SLei YU
13dcb3fd79SLei YUOptions:
14dcb3fd79SLei YU   -i, --image <squashfs|static>
15dcb3fd79SLei YU                          Generate SquashFS image or use static PNOR
16dcb3fd79SLei YU   -f, --file <file>      Specify destination file. Defaults to
17*7bd45a30SAdriana Kobylak                          $(pwd)/<PNOR FILE>.pnor.<image_type>.tar[.gz] if
18dcb3fd79SLei YU                          unspecified.
19dcb3fd79SLei YU                          (For example,
20dcb3fd79SLei YU                          * "generate-tar -i squashfs my.pnor" would generate
21*7bd45a30SAdriana Kobylak                          $(pwd)/my.pnor.squashfs.tar
22dcb3fd79SLei YU                          * "generate-tar -i static my.pnor" would generate
23*7bd45a30SAdriana Kobylak                          $(pwd)/my.pnor.static.tar.gz)
24dcb3fd79SLei YU   -s, --sign <path>      Sign the image. The optional path argument specifies
25dcb3fd79SLei YU                          the private key file. Defaults to the bash variable
26dcb3fd79SLei YU                          PRIVATE_KEY_PATH if available, or else uses the
27dcb3fd79SLei YU                          open-source private key in this script.
28647d6134SLei YU   -m, --machine <name>   Optionally specify the target machine name of this
29647d6134SLei YU                          image.
30dcb3fd79SLei YU   -h, --help             Display this help text and exit.
31dcb3fd79SLei YU'
32dcb3fd79SLei YU
33dcb3fd79SLei YUprivate_key=$'-----BEGIN PRIVATE KEY-----
34dcb3fd79SLei YUMIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPvSDLu6slkP1gri
35dcb3fd79SLei YUPaeQXL9ysD69J/HjbBCIQ0RPfeWBb75US1tRTjPP0Ub8CtH8ExVf8iF1ulsZA78B
36dcb3fd79SLei YUzIjBYZVp9pyD6LbpZ/hjV7rIH6dTNhoVpdA+F8LzmQ7cyhHG8l2JMvdunwF2uX5k
37dcb3fd79SLei YUD4WDcZt/ITKZNQNavPtmIyD5HprdAgMBAAECgYEAuQkTSi5ZNpAoWz76xtGRFSwU
38dcb3fd79SLei YUzUT4wQi3Mz6tDtjKTYXasiQGa0dHC1M9F8fDu6BZ9W7W4Dc9hArRcdzEighuxoI/
39dcb3fd79SLei YUnZI/0uL89iUEywnDEIHuS6D5JlZaj86/nx9YvQnO8F/seM+MX0EAWVrd5wC7aAF1
40dcb3fd79SLei YUh6Fu7ykZB4ggUjQAWwECQQD+AUiDOEO+8btLJ135dQfSGc5VFcZiequnKWVm6uXt
41dcb3fd79SLei YUrX771hEYjYMjLqWGFg9G4gE3GuABM5chMINuQQUivy8tAkEA/cxfy19XkjtqcMgE
42dcb3fd79SLei YUx/UDt6Nr+Ky/tk+4Y65WxPRDas0uxFOPk/vEjgVmz1k/TAy9G4giisluTvtmltr5
43dcb3fd79SLei YUDCLocQJBAJnRHx9PiD7uVhRJz6/L/iNuOzPtTsi+Loq5F83+O6T15qsM1CeBMsOw
44dcb3fd79SLei YUcM5FN5UeMcwz+yjfHAsePMkcmMaU7jUCQHlg9+N8upXuIo7Dqj2zOU7nMmkgvSNE
45dcb3fd79SLei YU5yuNImRZabC3ZolwaTdd7nf5r1y1Eyec5Ag5yENV6JKPe1Xkbb1XKJECQDngA0h4
46dcb3fd79SLei YU6ATvfP1Vrx4CbP11eKXbCsZ9OGPHSgyvVjn68oY5ZP3uPsIattoN7dE2BRfuJm7m
47dcb3fd79SLei YUF0nIdUAhR0yTfKM=
48dcb3fd79SLei YU-----END PRIVATE KEY-----
49dcb3fd79SLei YU'
50dcb3fd79SLei YU
51dcb3fd79SLei YU# Reference the ffs structures at:
52dcb3fd79SLei YU# https://github.com/open-power/hostboot/blob/master/src/usr/pnor/common/ffs_hb.H
53dcb3fd79SLei YU# https://github.com/open-power/hostboot/blob/master/src/usr/pnor/ffs.h
54*7bd45a30SAdriana Kobylakffs_entry_size=128
55*7bd45a30SAdriana Kobylakvercheck_offset=112
56dcb3fd79SLei YUdo_sign=false
57*7bd45a30SAdriana KobylakPRIVATE_KEY_PATH=${PRIVATE_KEY_PATH:-}
58dcb3fd79SLei YUprivate_key_path="${PRIVATE_KEY_PATH}"
59dcb3fd79SLei YUimage_type=""
60dcb3fd79SLei YUoutfile=""
61dcb3fd79SLei YUdeclare -a partitions=()
62dcb3fd79SLei YUtocfile="pnor.toc"
63647d6134SLei YUmachine_name=""
64dcb3fd79SLei YU
65dcb3fd79SLei YUwhile [[ $# -gt 0 ]]; do
66dcb3fd79SLei YU  key="$1"
67dcb3fd79SLei YU  case $key in
68dcb3fd79SLei YU    -i|--image)
69dcb3fd79SLei YU      image_type="$2"
70dcb3fd79SLei YU      shift 2
71dcb3fd79SLei YU      ;;
72dcb3fd79SLei YU    -f|--file)
73dcb3fd79SLei YU      outfile="$2"
74dcb3fd79SLei YU      shift 2
75dcb3fd79SLei YU      ;;
76dcb3fd79SLei YU    -s|--sign)
77dcb3fd79SLei YU      do_sign=true
78*7bd45a30SAdriana Kobylak      if [[ -n "${2}"  && "${2}" != -* ]]; then
79dcb3fd79SLei YU        private_key_path="$2"
80dcb3fd79SLei YU        shift 2
81dcb3fd79SLei YU      else
82dcb3fd79SLei YU        shift 1
83dcb3fd79SLei YU      fi
84dcb3fd79SLei YU      ;;
85647d6134SLei YU    -m|--machine)
86647d6134SLei YU      machine_name="$2"
87647d6134SLei YU      shift 2
88647d6134SLei YU      ;;
89dcb3fd79SLei YU    -h|--help)
90dcb3fd79SLei YU      echo "$help"
91dcb3fd79SLei YU      exit
92dcb3fd79SLei YU      ;;
93dcb3fd79SLei YU    *)
94dcb3fd79SLei YU      pnorfile="$1"
95dcb3fd79SLei YU      shift 1
96dcb3fd79SLei YU      ;;
97dcb3fd79SLei YU  esac
98dcb3fd79SLei YUdone
99dcb3fd79SLei YU
100dcb3fd79SLei YUif [ ! -f "${pnorfile}" ]; then
101dcb3fd79SLei YU  echo "Please enter a valid PNOR file."
102dcb3fd79SLei YU  echo "$help"
103dcb3fd79SLei YU  exit 1
104dcb3fd79SLei YUfi
105dcb3fd79SLei YU
106dcb3fd79SLei YUif [[ "${image_type}" == "squashfs" ]]; then
107dcb3fd79SLei YU  echo "Will generate squashfs image for VPNOR"
108dcb3fd79SLei YUelif [[ "${image_type}" == "static" ]]; then
109dcb3fd79SLei YU  echo "Will use static image for PNOR"
110dcb3fd79SLei YUelse
111dcb3fd79SLei YU  echo "Please specify the image type, \"squashfs\" or \"static\""
112dcb3fd79SLei YU  echo
113dcb3fd79SLei YU  echo "$help"
114dcb3fd79SLei YU  exit 1
115dcb3fd79SLei YUfi
116dcb3fd79SLei YU
117dcb3fd79SLei YUif [[ -z $outfile ]]; then
118dcb3fd79SLei YU    if [[ ${pnorfile##*.} == "pnor" ]]; then
119*7bd45a30SAdriana Kobylak        outfile=$(pwd)/${pnorfile##*/}.$image_type.tar
120dcb3fd79SLei YU    else
121*7bd45a30SAdriana Kobylak        outfile=$(pwd)/${pnorfile##*/}.pnor.$image_type.tar
122dcb3fd79SLei YU    fi
123dcb3fd79SLei YU    if [[ "${image_type}" == "static" ]]; then
124dcb3fd79SLei YU        # Append .gz so the tarball is compressed
125dcb3fd79SLei YU        outfile=$outfile.gz
126dcb3fd79SLei YU    fi
127dcb3fd79SLei YUelse
128dcb3fd79SLei YU    if [[ $outfile != /* ]]; then
129*7bd45a30SAdriana Kobylak        outfile=$(pwd)/$outfile
130dcb3fd79SLei YU    fi
131dcb3fd79SLei YUfi
132dcb3fd79SLei YU
133dcb3fd79SLei YU
134*7bd45a30SAdriana Kobylakscratch_dir=$(mktemp -d)
13500d8ade5SLei YU# Remove the temp directory on exit.
13600d8ade5SLei YU# The files in the temp directory may contain read-only files, so add
13700d8ade5SLei YU# --interactive=never to skip the prompt.
138*7bd45a30SAdriana Kobylaktrap '{ rm -r --interactive=never ${scratch_dir}; }' EXIT
139dcb3fd79SLei YU
140dcb3fd79SLei YUif [[ "${do_sign}" == true ]]; then
141dcb3fd79SLei YU  if [[ -z "${private_key_path}" ]]; then
142dcb3fd79SLei YU    private_key_path=${scratch_dir}/OpenBMC.priv
143dcb3fd79SLei YU    echo "${private_key}" > "${private_key_path}"
144dcb3fd79SLei YU    echo "Image is NOT secure!! Signing with the open private key!"
145dcb3fd79SLei YU  else
146dcb3fd79SLei YU    if [[ ! -f "${private_key_path}" ]]; then
147dcb3fd79SLei YU      echo "Couldn't find private key ${private_key_path}."
148dcb3fd79SLei YU      exit 1
149dcb3fd79SLei YU    fi
150dcb3fd79SLei YU
151dcb3fd79SLei YU    echo "Signing with ${private_key_path}."
152dcb3fd79SLei YU  fi
153dcb3fd79SLei YU
154dcb3fd79SLei YU  public_key_file=publickey
155dcb3fd79SLei YU  public_key_path=${scratch_dir}/$public_key_file
156*7bd45a30SAdriana Kobylak  openssl pkey -in "${private_key_path}" -pubout -out "${public_key_path}"
157dcb3fd79SLei YUfi
158dcb3fd79SLei YU
159dcb3fd79SLei YUecho "Parsing PNOR TOC..."
160dcb3fd79SLei YU
161851bc064SAdriana Kobylakpnor_dir="${scratch_dir}/pnor"
162*7bd45a30SAdriana Kobylakmkdir "${pnor_dir}"
163851bc064SAdriana Kobylak
164*7bd45a30SAdriana Kobylakpflash --partition=part --read="${pnor_dir}"/part -F "${pnorfile}"
165*7bd45a30SAdriana Kobylakpflash --partition=VERSION --read="${pnor_dir}"/VERSION -F "${pnorfile}"
166*7bd45a30SAdriana Kobylakversion_size=$(wc -c "${pnor_dir}"/VERSION | cut -d' ' -f 1)
167*7bd45a30SAdriana Kobylakmagic_number=$(xxd -p -l 4 "${pnor_dir}"/VERSION)
168dcb3fd79SLei YU# Check if VERSION is signed. A signed version partition will have an extra
169dcb3fd79SLei YU# 4K header starting with the magic number 0x17082011, see:
170dcb3fd79SLei YU# https://github.com/open-power/skiboot/blob/master/libstb/container.h#L47
171*7bd45a30SAdriana Kobylakif [ "$version_size" == "8192" ] && [ "$magic_number" == "17082011" ]; then
172dcb3fd79SLei YU  # Advance past the STB header (4K, indexed from 1)
173*7bd45a30SAdriana Kobylak  cp "${pnor_dir}"/VERSION "${pnor_dir}"/VERSION_FULL
174*7bd45a30SAdriana Kobylak  tail --bytes=+4097 "${pnor_dir}"/VERSION_FULL > "${pnor_dir}"/VERSION
175dcb3fd79SLei YUfi
176dcb3fd79SLei YU{
177*7bd45a30SAdriana Kobylak  version=$(head -n 1 "${pnor_dir}"/VERSION)
178dcb3fd79SLei YU  echo "version=$version"
179*7bd45a30SAdriana Kobylak  # shellcheck disable=SC2005,SC2046 # Need the echo to remove new lines, same
180*7bd45a30SAdriana Kobylak  # reason for not quoting the tail command
181*7bd45a30SAdriana Kobylak  extended_version=$(echo $(tail -n +2 "${pnor_dir}"/VERSION)|tr ' ' ',')
182dcb3fd79SLei YU  echo "extended_version=$extended_version"
183*7bd45a30SAdriana Kobylak  while read -r line; do
184dcb3fd79SLei YU    if [[ $line == "ID="* ]]; then
185dcb3fd79SLei YU        # This line looks like
186dcb3fd79SLei YU        # "ID=05 MVPD 000d9000..00169000 (actual=00090000) [ECC]"
187dcb3fd79SLei YU        read -r -a fields <<< "$line"
188dcb3fd79SLei YU
189dcb3fd79SLei YU        id=${fields[0]##*=}
190*7bd45a30SAdriana Kobylak        offset=$((ffs_entry_size * 10#${id} + vercheck_offset))
191*7bd45a30SAdriana Kobylak        vercheck=$(xxd -p -l  0x1 -seek ${offset} "${pnor_dir}"/part)
192*7bd45a30SAdriana Kobylak        # shellcheck disable=SC2155 # Need the export in the same line to avoid
193*7bd45a30SAdriana Kobylak        # pflash error
194*7bd45a30SAdriana Kobylak        export flags=$(pflash --detail=$((10#$id)) -F "${pnorfile}" | grep "\[" |
195dcb3fd79SLei YU                sed 's/....$//' | tr '\n' ',' | sed 's/.$//')
196dcb3fd79SLei YU        if [[ $flags != "" ]]; then
197dcb3fd79SLei YU            flags=,$flags
198dcb3fd79SLei YU        fi
199dcb3fd79SLei YU
200*7bd45a30SAdriana Kobylak        if [[ $(echo "$flags" | grep "READONLY") == "" &&
201*7bd45a30SAdriana Kobylak              $(echo "$flags" | grep "PRESERVED") == "" ]]; then
202dcb3fd79SLei YU            flags=$flags,READWRITE
203dcb3fd79SLei YU        fi
204dcb3fd79SLei YU
205dcb3fd79SLei YU        # Need the partition ID, name, start location, end location, and flags
206dcb3fd79SLei YU        echo  "partition${id}=${fields[1]},${fields[2]/../,},${vercheck}${flags}"
207dcb3fd79SLei YU
208dcb3fd79SLei YU        # Save the partition name
209*7bd45a30SAdriana Kobylak        partitions+=("${fields[1]}")
210dcb3fd79SLei YU    fi
211dcb3fd79SLei YU  # Don't need the BACKUP_PART partition
212*7bd45a30SAdriana Kobylak  done < <(pflash --info -F "${pnorfile}" | grep -v "BACKUP")
213*7bd45a30SAdriana Kobylak} > "${pnor_dir}"/${tocfile}
214dcb3fd79SLei YU
215dcb3fd79SLei YUfor partition in "${partitions[@]}"; do
216dcb3fd79SLei YU  echo "Reading ${partition}..."
217*7bd45a30SAdriana Kobylak  pflash --partition="${partition}" \
218*7bd45a30SAdriana Kobylak    --read="${pnor_dir}"/"${partition}" \
219*7bd45a30SAdriana Kobylak    -F "${pnorfile}"
220dcb3fd79SLei YUdone
221dcb3fd79SLei YU
222dcb3fd79SLei YUmanifest_location="MANIFEST"
223dcb3fd79SLei YUfiles_to_sign="$manifest_location $public_key_file"
224dcb3fd79SLei YU
225dcb3fd79SLei YU# Go to scratch_dir
226dcb3fd79SLei YU
227dcb3fd79SLei YUif [[ "${image_type}" == "squashfs" ]]; then
228dcb3fd79SLei YU  echo "Creating SquashFS image..."
229851bc064SAdriana Kobylak  # Prepare pnor file in ${pnor_dir}
230851bc064SAdriana Kobylak  cd "${pnor_dir}"
23185f25407SAdriana Kobylak  # Set permissions of partition files to read only
232*7bd45a30SAdriana Kobylak  chmod 440 -- *
233*7bd45a30SAdriana Kobylak  # shellcheck disable=SC2086 # Do not quote partitions since it lists multiple
234*7bd45a30SAdriana Kobylak  # files and mksquashfs would assume to be a single file name within quotes
235*7bd45a30SAdriana Kobylak  mksquashfs ${tocfile} ${partitions[*]} "${scratch_dir}"/pnor.xz.squashfs -all-root
236dcb3fd79SLei YU  cd "${scratch_dir}"
237dcb3fd79SLei YU  files_to_sign+=" pnor.xz.squashfs"
238dcb3fd79SLei YUelse
239*7bd45a30SAdriana Kobylak  cp "${pnorfile}" "${scratch_dir}"
240dcb3fd79SLei YU  cd "${scratch_dir}"
241*7bd45a30SAdriana Kobylak  files_to_sign+=" $(basename "${pnorfile}")"
242dcb3fd79SLei YUfi
243dcb3fd79SLei YU
244dcb3fd79SLei YUecho "Creating MANIFEST for the image"
245dcb3fd79SLei YUecho -e "purpose=xyz.openbmc_project.Software.Version.VersionPurpose.Host\nversion=$version\n\
246dcb3fd79SLei YUextended_version=$extended_version" >> $manifest_location
247dcb3fd79SLei YU
248*7bd45a30SAdriana Kobylakif [[ -n "${machine_name}" ]]; then
249647d6134SLei YU    echo -e "MachineName=${machine_name}" >> $manifest_location
250647d6134SLei YUfi
251647d6134SLei YU
252dcb3fd79SLei YUif [[ "${do_sign}" == true ]]; then
253dcb3fd79SLei YU  private_key_name=$(basename "${private_key_path}")
254dcb3fd79SLei YU  key_type="${private_key_name%.*}"
255dcb3fd79SLei YU  echo KeyType="${key_type}" >> $manifest_location
256dcb3fd79SLei YU  echo HashType="RSA-SHA256" >> $manifest_location
257dcb3fd79SLei YU
258dcb3fd79SLei YU  for file in $files_to_sign; do
259*7bd45a30SAdriana Kobylak    openssl dgst -sha256 -sign "${private_key_path}" -out "${file}.sig" "$file"
260dcb3fd79SLei YU  done
261dcb3fd79SLei YU
262dcb3fd79SLei YU  additional_files="*.sig"
263dcb3fd79SLei YUfi
264dcb3fd79SLei YU
265dcb3fd79SLei YUif [[ "${image_type}" == "squashfs" ]]; then
266dcb3fd79SLei YU  echo "Generating tarball to contain the SquashFS image and its MANIFEST"
267*7bd45a30SAdriana Kobylak  # shellcheck disable=SC2086 # Do not quote the files variables since they list
268*7bd45a30SAdriana Kobylak  # multiple files and tar would assume to be a single file name within quotes
269*7bd45a30SAdriana Kobylak  tar -cvf "$outfile" $files_to_sign $additional_files
270dcb3fd79SLei YU  echo "SquashFSTarball at ${outfile}"
271dcb3fd79SLei YUelse
272*7bd45a30SAdriana Kobylak  # shellcheck disable=SC2086 # Do not quote the files variables since they list
273*7bd45a30SAdriana Kobylak  # multiple files and tar would assume to be a single file name within quotes
274*7bd45a30SAdriana Kobylak  tar -czvf "$outfile" $files_to_sign $additional_files
275dcb3fd79SLei YU  echo "Static layout tarball at $outfile"
276dcb3fd79SLei YUfi
277