1#!/bin/bash 2set -eo pipefail 3 4help=$'Generate Tarball with PNOR image and MANIFEST Script 5 6Generates a PNOR SquashFS image from the PNOR image for VPNOR, 7Or use a static layout raw PNOR image, 8Creates a MANIFEST for image verification and recreation 9Packages the image and MANIFEST together in a tarball 10 11usage: generate-tar [OPTION] <PNOR FILE>... 12 13Options: 14 -i, --image <squashfs|static> 15 Generate SquashFS image or use static PNOR 16 -f, --file <file> Specify destination file. Defaults to 17 `pwd`/<PNOR FILE>.pnor.<image_type>.tar[.gz] if 18 unspecified. 19 (For example, 20 * "generate-tar -i squashfs my.pnor" would generate 21 `pwd`/my.pnor.squashfs.tar 22 * "generate-tar -i static my.pnor" would generate 23 `pwd`/my.pnor.static.tar.gz) 24 -s, --sign <path> Sign the image. The optional path argument specifies 25 the private key file. Defaults to the bash variable 26 PRIVATE_KEY_PATH if available, or else uses the 27 open-source private key in this script. 28 -h, --help Display this help text and exit. 29' 30 31private_key=$'-----BEGIN PRIVATE KEY----- 32MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPvSDLu6slkP1gri 33PaeQXL9ysD69J/HjbBCIQ0RPfeWBb75US1tRTjPP0Ub8CtH8ExVf8iF1ulsZA78B 34zIjBYZVp9pyD6LbpZ/hjV7rIH6dTNhoVpdA+F8LzmQ7cyhHG8l2JMvdunwF2uX5k 35D4WDcZt/ITKZNQNavPtmIyD5HprdAgMBAAECgYEAuQkTSi5ZNpAoWz76xtGRFSwU 36zUT4wQi3Mz6tDtjKTYXasiQGa0dHC1M9F8fDu6BZ9W7W4Dc9hArRcdzEighuxoI/ 37nZI/0uL89iUEywnDEIHuS6D5JlZaj86/nx9YvQnO8F/seM+MX0EAWVrd5wC7aAF1 38h6Fu7ykZB4ggUjQAWwECQQD+AUiDOEO+8btLJ135dQfSGc5VFcZiequnKWVm6uXt 39rX771hEYjYMjLqWGFg9G4gE3GuABM5chMINuQQUivy8tAkEA/cxfy19XkjtqcMgE 40x/UDt6Nr+Ky/tk+4Y65WxPRDas0uxFOPk/vEjgVmz1k/TAy9G4giisluTvtmltr5 41DCLocQJBAJnRHx9PiD7uVhRJz6/L/iNuOzPtTsi+Loq5F83+O6T15qsM1CeBMsOw 42cM5FN5UeMcwz+yjfHAsePMkcmMaU7jUCQHlg9+N8upXuIo7Dqj2zOU7nMmkgvSNE 435yuNImRZabC3ZolwaTdd7nf5r1y1Eyec5Ag5yENV6JKPe1Xkbb1XKJECQDngA0h4 446ATvfP1Vrx4CbP11eKXbCsZ9OGPHSgyvVjn68oY5ZP3uPsIattoN7dE2BRfuJm7m 45F0nIdUAhR0yTfKM= 46-----END PRIVATE KEY----- 47' 48 49# Reference the ffs structures at: 50# https://github.com/open-power/hostboot/blob/master/src/usr/pnor/common/ffs_hb.H 51# https://github.com/open-power/hostboot/blob/master/src/usr/pnor/ffs.h 52let ffs_entry_size=128 53let vercheck_offset=112 54do_sign=false 55private_key_path="${PRIVATE_KEY_PATH}" 56image_type="" 57outfile="" 58declare -a partitions=() 59tocfile="pnor.toc" 60 61while [[ $# -gt 0 ]]; do 62 key="$1" 63 case $key in 64 -i|--image) 65 image_type="$2" 66 shift 2 67 ;; 68 -f|--file) 69 outfile="$2" 70 shift 2 71 ;; 72 -s|--sign) 73 do_sign=true 74 if [[ ! -z "${2}" && "${2}" != -* ]]; then 75 private_key_path="$2" 76 shift 2 77 else 78 shift 1 79 fi 80 ;; 81 -h|--help) 82 echo "$help" 83 exit 84 ;; 85 *) 86 pnorfile="$1" 87 shift 1 88 ;; 89 esac 90done 91 92if [ ! -f "${pnorfile}" ]; then 93 echo "Please enter a valid PNOR file." 94 echo "$help" 95 exit 1 96fi 97 98if [[ "${image_type}" == "squashfs" ]]; then 99 echo "Will generate squashfs image for VPNOR" 100elif [[ "${image_type}" == "static" ]]; then 101 echo "Will use static image for PNOR" 102else 103 echo "Please specify the image type, \"squashfs\" or \"static\"" 104 echo 105 echo "$help" 106 exit 1 107fi 108 109if [[ -z $outfile ]]; then 110 if [[ ${pnorfile##*.} == "pnor" ]]; then 111 outfile=`pwd`/${pnorfile##*/}.$image_type.tar 112 else 113 outfile=`pwd`/${pnorfile##*/}.pnor.$image_type.tar 114 fi 115 if [[ "${image_type}" == "static" ]]; then 116 # Append .gz so the tarball is compressed 117 outfile=$outfile.gz 118 fi 119else 120 if [[ $outfile != /* ]]; then 121 outfile=`pwd`/$outfile 122 fi 123fi 124 125 126scratch_dir=`mktemp -d` 127 128if [[ "${do_sign}" == true ]]; then 129 if [[ -z "${private_key_path}" ]]; then 130 private_key_path=${scratch_dir}/OpenBMC.priv 131 echo "${private_key}" > "${private_key_path}" 132 echo "Image is NOT secure!! Signing with the open private key!" 133 else 134 if [[ ! -f "${private_key_path}" ]]; then 135 echo "Couldn't find private key ${private_key_path}." 136 exit 1 137 fi 138 139 echo "Signing with ${private_key_path}." 140 fi 141 142 public_key_file=publickey 143 public_key_path=${scratch_dir}/$public_key_file 144 openssl pkey -in "${private_key_path}" -pubout -out ${public_key_path} 145fi 146 147echo "Parsing PNOR TOC..." 148 149pflash --partition=part --read=${scratch_dir}/part -F ${pnorfile} 150pflash --partition=VERSION --read=${scratch_dir}/VERSION -F ${pnorfile} 151version_size=$(du -k ${scratch_dir}/VERSION | head -1 | cut -f 1) 152magic_number=$(xxd -p -l 4 ${scratch_dir}/VERSION) 153# Check if VERSION is signed. A signed version partition will have an extra 154# 4K header starting with the magic number 0x17082011, see: 155# https://github.com/open-power/skiboot/blob/master/libstb/container.h#L47 156if [ "$version_size" == "8" -a "$magic_number" == "17082011" ]; then 157 # Advance past the STB header (4K, indexed from 1) 158 cp ${scratch_dir}/VERSION ${scratch_dir}/VERSION_FULL 159 tail --bytes=+4097 ${scratch_dir}/VERSION_FULL > ${scratch_dir}/VERSION 160fi 161{ 162 version=$(head -n 1 ${scratch_dir}/VERSION) 163 echo "version=$version" 164 extended_version=$(echo $(tail -n +2 ${scratch_dir}/VERSION)|tr ' ' ',') 165 echo "extended_version=$extended_version" 166 while read line; do 167 if [[ $line == "ID="* ]]; then 168 # This line looks like 169 # "ID=05 MVPD 000d9000..00169000 (actual=00090000) [ECC]" 170 read -r -a fields <<< "$line" 171 172 id=${fields[0]##*=} 173 offset=$((${ffs_entry_size} * 10#${id} + ${vercheck_offset})) 174 vercheck=$(xxd -p -l 0x1 -seek ${offset} ${scratch_dir}/part) 175 export flags=$(pflash --detail=$((10#$id)) -F ${pnorfile} | grep "\[" | 176 sed 's/....$//' | tr '\n' ',' | sed 's/.$//') 177 if [[ $flags != "" ]]; then 178 flags=,$flags 179 fi 180 181 if [[ $(echo $flags | grep "READONLY") == "" && 182 $(echo $flags | grep "PRESERVED") == "" ]]; then 183 flags=$flags,READWRITE 184 fi 185 186 # Need the partition ID, name, start location, end location, and flags 187 echo "partition${id}=${fields[1]},${fields[2]/../,},${vercheck}${flags}" 188 189 # Save the partition name 190 partitions+=(${fields[1]}) 191 fi 192 # Don't need the BACKUP_PART partition 193 done < <(pflash --info -F ${pnorfile} | grep -v "BACKUP") 194} > ${scratch_dir}/${tocfile} 195 196for partition in "${partitions[@]}"; do 197 echo "Reading ${partition}..." 198 pflash --partition=${partition} \ 199 --read=${scratch_dir}/${partition} \ 200 -F ${pnorfile} 201done 202 203manifest_location="MANIFEST" 204files_to_sign="$manifest_location $public_key_file" 205 206# Go to scratch_dir 207 208if [[ "${image_type}" == "squashfs" ]]; then 209 echo "Creating SquashFS image..." 210 # Prepare pnor file in scratch_dir 211 cd "${scratch_dir}" 212 mksquashfs ${tocfile} ${partitions[*]} pnor.xz.squashfs 213 files_to_sign+=" pnor.xz.squashfs" 214else 215 cp ${pnorfile} ${scratch_dir} 216 cd "${scratch_dir}" 217 files_to_sign+=" $(basename ${pnorfile})" 218fi 219 220echo "Creating MANIFEST for the image" 221echo -e "purpose=xyz.openbmc_project.Software.Version.VersionPurpose.Host\nversion=$version\n\ 222extended_version=$extended_version" >> $manifest_location 223 224if [[ "${do_sign}" == true ]]; then 225 private_key_name=$(basename "${private_key_path}") 226 key_type="${private_key_name%.*}" 227 echo KeyType="${key_type}" >> $manifest_location 228 echo HashType="RSA-SHA256" >> $manifest_location 229 230 for file in $files_to_sign; do 231 openssl dgst -sha256 -sign ${private_key_path} -out "${file}.sig" $file 232 done 233 234 additional_files="*.sig" 235fi 236 237if [[ "${image_type}" == "squashfs" ]]; then 238 echo "Generating tarball to contain the SquashFS image and its MANIFEST" 239 tar -cvf $outfile $files_to_sign $additional_files 240 echo "SquashFSTarball at ${outfile}" 241else 242 tar -czvf $outfile $files_to_sign $additional_files 243 echo "Static layout tarball at $outfile" 244fi 245 246rm -r "${scratch_dir}" 247