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` 127trap "{ rm -r ${scratch_dir}; }" EXIT 128 129if [[ "${do_sign}" == true ]]; then 130 if [[ -z "${private_key_path}" ]]; then 131 private_key_path=${scratch_dir}/OpenBMC.priv 132 echo "${private_key}" > "${private_key_path}" 133 echo "Image is NOT secure!! Signing with the open private key!" 134 else 135 if [[ ! -f "${private_key_path}" ]]; then 136 echo "Couldn't find private key ${private_key_path}." 137 exit 1 138 fi 139 140 echo "Signing with ${private_key_path}." 141 fi 142 143 public_key_file=publickey 144 public_key_path=${scratch_dir}/$public_key_file 145 openssl pkey -in "${private_key_path}" -pubout -out ${public_key_path} 146fi 147 148echo "Parsing PNOR TOC..." 149 150pnor_dir="${scratch_dir}/pnor" 151mkdir ${pnor_dir} 152 153pflash --partition=part --read=${pnor_dir}/part -F ${pnorfile} 154pflash --partition=VERSION --read=${pnor_dir}/VERSION -F ${pnorfile} 155version_size=$(wc -c ${pnor_dir}/VERSION | cut -d' ' -f 1) 156magic_number=$(xxd -p -l 4 ${pnor_dir}/VERSION) 157# Check if VERSION is signed. A signed version partition will have an extra 158# 4K header starting with the magic number 0x17082011, see: 159# https://github.com/open-power/skiboot/blob/master/libstb/container.h#L47 160if [ "$version_size" == "8192" -a "$magic_number" == "17082011" ]; then 161 # Advance past the STB header (4K, indexed from 1) 162 cp ${pnor_dir}/VERSION ${pnor_dir}/VERSION_FULL 163 tail --bytes=+4097 ${pnor_dir}/VERSION_FULL > ${pnor_dir}/VERSION 164fi 165{ 166 version=$(head -n 1 ${pnor_dir}/VERSION) 167 echo "version=$version" 168 extended_version=$(echo $(tail -n +2 ${pnor_dir}/VERSION)|tr ' ' ',') 169 echo "extended_version=$extended_version" 170 while read line; do 171 if [[ $line == "ID="* ]]; then 172 # This line looks like 173 # "ID=05 MVPD 000d9000..00169000 (actual=00090000) [ECC]" 174 read -r -a fields <<< "$line" 175 176 id=${fields[0]##*=} 177 offset=$((${ffs_entry_size} * 10#${id} + ${vercheck_offset})) 178 vercheck=$(xxd -p -l 0x1 -seek ${offset} ${pnor_dir}/part) 179 export flags=$(pflash --detail=$((10#$id)) -F ${pnorfile} | grep "\[" | 180 sed 's/....$//' | tr '\n' ',' | sed 's/.$//') 181 if [[ $flags != "" ]]; then 182 flags=,$flags 183 fi 184 185 if [[ $(echo $flags | grep "READONLY") == "" && 186 $(echo $flags | grep "PRESERVED") == "" ]]; then 187 flags=$flags,READWRITE 188 fi 189 190 # Need the partition ID, name, start location, end location, and flags 191 echo "partition${id}=${fields[1]},${fields[2]/../,},${vercheck}${flags}" 192 193 # Save the partition name 194 partitions+=(${fields[1]}) 195 fi 196 # Don't need the BACKUP_PART partition 197 done < <(pflash --info -F ${pnorfile} | grep -v "BACKUP") 198} > ${pnor_dir}/${tocfile} 199 200for partition in "${partitions[@]}"; do 201 echo "Reading ${partition}..." 202 pflash --partition=${partition} \ 203 --read=${pnor_dir}/${partition} \ 204 -F ${pnorfile} 205done 206 207manifest_location="MANIFEST" 208files_to_sign="$manifest_location $public_key_file" 209 210# Go to scratch_dir 211 212if [[ "${image_type}" == "squashfs" ]]; then 213 echo "Creating SquashFS image..." 214 # Prepare pnor file in ${pnor_dir} 215 cd "${pnor_dir}" 216 # Set permissions of partition files to read only 217 chmod 440 * 218 mksquashfs ${tocfile} ${partitions[*]} ${scratch_dir}/pnor.xz.squashfs -all-root 219 cd "${scratch_dir}" 220 files_to_sign+=" pnor.xz.squashfs" 221else 222 cp ${pnorfile} ${scratch_dir} 223 cd "${scratch_dir}" 224 files_to_sign+=" $(basename ${pnorfile})" 225fi 226 227echo "Creating MANIFEST for the image" 228echo -e "purpose=xyz.openbmc_project.Software.Version.VersionPurpose.Host\nversion=$version\n\ 229extended_version=$extended_version" >> $manifest_location 230 231if [[ "${do_sign}" == true ]]; then 232 private_key_name=$(basename "${private_key_path}") 233 key_type="${private_key_name%.*}" 234 echo KeyType="${key_type}" >> $manifest_location 235 echo HashType="RSA-SHA256" >> $manifest_location 236 237 for file in $files_to_sign; do 238 openssl dgst -sha256 -sign ${private_key_path} -out "${file}.sig" $file 239 done 240 241 additional_files="*.sig" 242fi 243 244if [[ "${image_type}" == "squashfs" ]]; then 245 echo "Generating tarball to contain the SquashFS image and its MANIFEST" 246 tar -cvf $outfile $files_to_sign $additional_files 247 echo "SquashFSTarball at ${outfile}" 248else 249 tar -czvf $outfile $files_to_sign $additional_files 250 echo "Static layout tarball at $outfile" 251fi 252 253