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