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 17dcb3fd79SLei YU `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 21dcb3fd79SLei YU `pwd`/my.pnor.squashfs.tar 22dcb3fd79SLei YU * "generate-tar -i static my.pnor" would generate 23dcb3fd79SLei YU `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. 28dcb3fd79SLei YU -h, --help Display this help text and exit. 29dcb3fd79SLei YU' 30dcb3fd79SLei YU 31dcb3fd79SLei YUprivate_key=$'-----BEGIN PRIVATE KEY----- 32dcb3fd79SLei YUMIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPvSDLu6slkP1gri 33dcb3fd79SLei YUPaeQXL9ysD69J/HjbBCIQ0RPfeWBb75US1tRTjPP0Ub8CtH8ExVf8iF1ulsZA78B 34dcb3fd79SLei YUzIjBYZVp9pyD6LbpZ/hjV7rIH6dTNhoVpdA+F8LzmQ7cyhHG8l2JMvdunwF2uX5k 35dcb3fd79SLei YUD4WDcZt/ITKZNQNavPtmIyD5HprdAgMBAAECgYEAuQkTSi5ZNpAoWz76xtGRFSwU 36dcb3fd79SLei YUzUT4wQi3Mz6tDtjKTYXasiQGa0dHC1M9F8fDu6BZ9W7W4Dc9hArRcdzEighuxoI/ 37dcb3fd79SLei YUnZI/0uL89iUEywnDEIHuS6D5JlZaj86/nx9YvQnO8F/seM+MX0EAWVrd5wC7aAF1 38dcb3fd79SLei YUh6Fu7ykZB4ggUjQAWwECQQD+AUiDOEO+8btLJ135dQfSGc5VFcZiequnKWVm6uXt 39dcb3fd79SLei YUrX771hEYjYMjLqWGFg9G4gE3GuABM5chMINuQQUivy8tAkEA/cxfy19XkjtqcMgE 40dcb3fd79SLei YUx/UDt6Nr+Ky/tk+4Y65WxPRDas0uxFOPk/vEjgVmz1k/TAy9G4giisluTvtmltr5 41dcb3fd79SLei YUDCLocQJBAJnRHx9PiD7uVhRJz6/L/iNuOzPtTsi+Loq5F83+O6T15qsM1CeBMsOw 42dcb3fd79SLei YUcM5FN5UeMcwz+yjfHAsePMkcmMaU7jUCQHlg9+N8upXuIo7Dqj2zOU7nMmkgvSNE 43dcb3fd79SLei YU5yuNImRZabC3ZolwaTdd7nf5r1y1Eyec5Ag5yENV6JKPe1Xkbb1XKJECQDngA0h4 44dcb3fd79SLei YU6ATvfP1Vrx4CbP11eKXbCsZ9OGPHSgyvVjn68oY5ZP3uPsIattoN7dE2BRfuJm7m 45dcb3fd79SLei YUF0nIdUAhR0yTfKM= 46dcb3fd79SLei YU-----END PRIVATE KEY----- 47dcb3fd79SLei YU' 48dcb3fd79SLei YU 49dcb3fd79SLei YU# Reference the ffs structures at: 50dcb3fd79SLei YU# https://github.com/open-power/hostboot/blob/master/src/usr/pnor/common/ffs_hb.H 51dcb3fd79SLei YU# https://github.com/open-power/hostboot/blob/master/src/usr/pnor/ffs.h 52dcb3fd79SLei YUlet ffs_entry_size=128 53dcb3fd79SLei YUlet vercheck_offset=112 54dcb3fd79SLei YUdo_sign=false 55dcb3fd79SLei YUprivate_key_path="${PRIVATE_KEY_PATH}" 56dcb3fd79SLei YUimage_type="" 57dcb3fd79SLei YUoutfile="" 58dcb3fd79SLei YUdeclare -a partitions=() 59dcb3fd79SLei YUtocfile="pnor.toc" 60dcb3fd79SLei YU 61dcb3fd79SLei YUwhile [[ $# -gt 0 ]]; do 62dcb3fd79SLei YU key="$1" 63dcb3fd79SLei YU case $key in 64dcb3fd79SLei YU -i|--image) 65dcb3fd79SLei YU image_type="$2" 66dcb3fd79SLei YU shift 2 67dcb3fd79SLei YU ;; 68dcb3fd79SLei YU -f|--file) 69dcb3fd79SLei YU outfile="$2" 70dcb3fd79SLei YU shift 2 71dcb3fd79SLei YU ;; 72dcb3fd79SLei YU -s|--sign) 73dcb3fd79SLei YU do_sign=true 74dcb3fd79SLei YU if [[ ! -z "${2}" && "${2}" != -* ]]; then 75dcb3fd79SLei YU private_key_path="$2" 76dcb3fd79SLei YU shift 2 77dcb3fd79SLei YU else 78dcb3fd79SLei YU shift 1 79dcb3fd79SLei YU fi 80dcb3fd79SLei YU ;; 81dcb3fd79SLei YU -h|--help) 82dcb3fd79SLei YU echo "$help" 83dcb3fd79SLei YU exit 84dcb3fd79SLei YU ;; 85dcb3fd79SLei YU *) 86dcb3fd79SLei YU pnorfile="$1" 87dcb3fd79SLei YU shift 1 88dcb3fd79SLei YU ;; 89dcb3fd79SLei YU esac 90dcb3fd79SLei YUdone 91dcb3fd79SLei YU 92dcb3fd79SLei YUif [ ! -f "${pnorfile}" ]; then 93dcb3fd79SLei YU echo "Please enter a valid PNOR file." 94dcb3fd79SLei YU echo "$help" 95dcb3fd79SLei YU exit 1 96dcb3fd79SLei YUfi 97dcb3fd79SLei YU 98dcb3fd79SLei YUif [[ "${image_type}" == "squashfs" ]]; then 99dcb3fd79SLei YU echo "Will generate squashfs image for VPNOR" 100dcb3fd79SLei YUelif [[ "${image_type}" == "static" ]]; then 101dcb3fd79SLei YU echo "Will use static image for PNOR" 102dcb3fd79SLei YUelse 103dcb3fd79SLei YU echo "Please specify the image type, \"squashfs\" or \"static\"" 104dcb3fd79SLei YU echo 105dcb3fd79SLei YU echo "$help" 106dcb3fd79SLei YU exit 1 107dcb3fd79SLei YUfi 108dcb3fd79SLei YU 109dcb3fd79SLei YUif [[ -z $outfile ]]; then 110dcb3fd79SLei YU if [[ ${pnorfile##*.} == "pnor" ]]; then 111dcb3fd79SLei YU outfile=`pwd`/${pnorfile##*/}.$image_type.tar 112dcb3fd79SLei YU else 113dcb3fd79SLei YU outfile=`pwd`/${pnorfile##*/}.pnor.$image_type.tar 114dcb3fd79SLei YU fi 115dcb3fd79SLei YU if [[ "${image_type}" == "static" ]]; then 116dcb3fd79SLei YU # Append .gz so the tarball is compressed 117dcb3fd79SLei YU outfile=$outfile.gz 118dcb3fd79SLei YU fi 119dcb3fd79SLei YUelse 120dcb3fd79SLei YU if [[ $outfile != /* ]]; then 121dcb3fd79SLei YU outfile=`pwd`/$outfile 122dcb3fd79SLei YU fi 123dcb3fd79SLei YUfi 124dcb3fd79SLei YU 125dcb3fd79SLei YU 126dcb3fd79SLei YUscratch_dir=`mktemp -d` 1278004ff59SAdriana Kobylaktrap "{ rm -r ${scratch_dir}; }" EXIT 128dcb3fd79SLei YU 129dcb3fd79SLei YUif [[ "${do_sign}" == true ]]; then 130dcb3fd79SLei YU if [[ -z "${private_key_path}" ]]; then 131dcb3fd79SLei YU private_key_path=${scratch_dir}/OpenBMC.priv 132dcb3fd79SLei YU echo "${private_key}" > "${private_key_path}" 133dcb3fd79SLei YU echo "Image is NOT secure!! Signing with the open private key!" 134dcb3fd79SLei YU else 135dcb3fd79SLei YU if [[ ! -f "${private_key_path}" ]]; then 136dcb3fd79SLei YU echo "Couldn't find private key ${private_key_path}." 137dcb3fd79SLei YU exit 1 138dcb3fd79SLei YU fi 139dcb3fd79SLei YU 140dcb3fd79SLei YU echo "Signing with ${private_key_path}." 141dcb3fd79SLei YU fi 142dcb3fd79SLei YU 143dcb3fd79SLei YU public_key_file=publickey 144dcb3fd79SLei YU public_key_path=${scratch_dir}/$public_key_file 145dcb3fd79SLei YU openssl pkey -in "${private_key_path}" -pubout -out ${public_key_path} 146dcb3fd79SLei YUfi 147dcb3fd79SLei YU 148dcb3fd79SLei YUecho "Parsing PNOR TOC..." 149dcb3fd79SLei YU 150*851bc064SAdriana Kobylakpnor_dir="${scratch_dir}/pnor" 151*851bc064SAdriana Kobylakmkdir ${pnor_dir} 152*851bc064SAdriana Kobylak 153*851bc064SAdriana Kobylakpflash --partition=part --read=${pnor_dir}/part -F ${pnorfile} 154*851bc064SAdriana Kobylakpflash --partition=VERSION --read=${pnor_dir}/VERSION -F ${pnorfile} 155*851bc064SAdriana Kobylakversion_size=$(du -k ${pnor_dir}/VERSION | head -1 | cut -f 1) 156*851bc064SAdriana Kobylakmagic_number=$(xxd -p -l 4 ${pnor_dir}/VERSION) 157dcb3fd79SLei YU# Check if VERSION is signed. A signed version partition will have an extra 158dcb3fd79SLei YU# 4K header starting with the magic number 0x17082011, see: 159dcb3fd79SLei YU# https://github.com/open-power/skiboot/blob/master/libstb/container.h#L47 160dcb3fd79SLei YUif [ "$version_size" == "8" -a "$magic_number" == "17082011" ]; then 161dcb3fd79SLei YU # Advance past the STB header (4K, indexed from 1) 162*851bc064SAdriana Kobylak cp ${pnor_dir}/VERSION ${pnor_dir}/VERSION_FULL 163*851bc064SAdriana Kobylak tail --bytes=+4097 ${pnor_dir}/VERSION_FULL > ${pnor_dir}/VERSION 164dcb3fd79SLei YUfi 165dcb3fd79SLei YU{ 166*851bc064SAdriana Kobylak version=$(head -n 1 ${pnor_dir}/VERSION) 167dcb3fd79SLei YU echo "version=$version" 168*851bc064SAdriana Kobylak extended_version=$(echo $(tail -n +2 ${pnor_dir}/VERSION)|tr ' ' ',') 169dcb3fd79SLei YU echo "extended_version=$extended_version" 170dcb3fd79SLei YU while read line; do 171dcb3fd79SLei YU if [[ $line == "ID="* ]]; then 172dcb3fd79SLei YU # This line looks like 173dcb3fd79SLei YU # "ID=05 MVPD 000d9000..00169000 (actual=00090000) [ECC]" 174dcb3fd79SLei YU read -r -a fields <<< "$line" 175dcb3fd79SLei YU 176dcb3fd79SLei YU id=${fields[0]##*=} 177dcb3fd79SLei YU offset=$((${ffs_entry_size} * 10#${id} + ${vercheck_offset})) 178*851bc064SAdriana Kobylak vercheck=$(xxd -p -l 0x1 -seek ${offset} ${pnor_dir}/part) 179dcb3fd79SLei YU export flags=$(pflash --detail=$((10#$id)) -F ${pnorfile} | grep "\[" | 180dcb3fd79SLei YU sed 's/....$//' | tr '\n' ',' | sed 's/.$//') 181dcb3fd79SLei YU if [[ $flags != "" ]]; then 182dcb3fd79SLei YU flags=,$flags 183dcb3fd79SLei YU fi 184dcb3fd79SLei YU 185dcb3fd79SLei YU if [[ $(echo $flags | grep "READONLY") == "" && 186dcb3fd79SLei YU $(echo $flags | grep "PRESERVED") == "" ]]; then 187dcb3fd79SLei YU flags=$flags,READWRITE 188dcb3fd79SLei YU fi 189dcb3fd79SLei YU 190dcb3fd79SLei YU # Need the partition ID, name, start location, end location, and flags 191dcb3fd79SLei YU echo "partition${id}=${fields[1]},${fields[2]/../,},${vercheck}${flags}" 192dcb3fd79SLei YU 193dcb3fd79SLei YU # Save the partition name 194dcb3fd79SLei YU partitions+=(${fields[1]}) 195dcb3fd79SLei YU fi 196dcb3fd79SLei YU # Don't need the BACKUP_PART partition 197dcb3fd79SLei YU done < <(pflash --info -F ${pnorfile} | grep -v "BACKUP") 198*851bc064SAdriana Kobylak} > ${pnor_dir}/${tocfile} 199dcb3fd79SLei YU 200dcb3fd79SLei YUfor partition in "${partitions[@]}"; do 201dcb3fd79SLei YU echo "Reading ${partition}..." 202dcb3fd79SLei YU pflash --partition=${partition} \ 203*851bc064SAdriana Kobylak --read=${pnor_dir}/${partition} \ 204dcb3fd79SLei YU -F ${pnorfile} 205dcb3fd79SLei YUdone 206dcb3fd79SLei YU 207dcb3fd79SLei YUmanifest_location="MANIFEST" 208dcb3fd79SLei YUfiles_to_sign="$manifest_location $public_key_file" 209dcb3fd79SLei YU 210dcb3fd79SLei YU# Go to scratch_dir 211dcb3fd79SLei YU 212dcb3fd79SLei YUif [[ "${image_type}" == "squashfs" ]]; then 213dcb3fd79SLei YU echo "Creating SquashFS image..." 214*851bc064SAdriana Kobylak # Prepare pnor file in ${pnor_dir} 215*851bc064SAdriana Kobylak cd "${pnor_dir}" 216*851bc064SAdriana Kobylak mksquashfs ${tocfile} ${partitions[*]} ${scratch_dir}/pnor.xz.squashfs 217dcb3fd79SLei YU cd "${scratch_dir}" 218dcb3fd79SLei YU files_to_sign+=" pnor.xz.squashfs" 219dcb3fd79SLei YUelse 220dcb3fd79SLei YU cp ${pnorfile} ${scratch_dir} 221dcb3fd79SLei YU cd "${scratch_dir}" 222dcb3fd79SLei YU files_to_sign+=" $(basename ${pnorfile})" 223dcb3fd79SLei YUfi 224dcb3fd79SLei YU 225dcb3fd79SLei YUecho "Creating MANIFEST for the image" 226dcb3fd79SLei YUecho -e "purpose=xyz.openbmc_project.Software.Version.VersionPurpose.Host\nversion=$version\n\ 227dcb3fd79SLei YUextended_version=$extended_version" >> $manifest_location 228dcb3fd79SLei YU 229dcb3fd79SLei YUif [[ "${do_sign}" == true ]]; then 230dcb3fd79SLei YU private_key_name=$(basename "${private_key_path}") 231dcb3fd79SLei YU key_type="${private_key_name%.*}" 232dcb3fd79SLei YU echo KeyType="${key_type}" >> $manifest_location 233dcb3fd79SLei YU echo HashType="RSA-SHA256" >> $manifest_location 234dcb3fd79SLei YU 235dcb3fd79SLei YU for file in $files_to_sign; do 236dcb3fd79SLei YU openssl dgst -sha256 -sign ${private_key_path} -out "${file}.sig" $file 237dcb3fd79SLei YU done 238dcb3fd79SLei YU 239dcb3fd79SLei YU additional_files="*.sig" 240dcb3fd79SLei YUfi 241dcb3fd79SLei YU 242dcb3fd79SLei YUif [[ "${image_type}" == "squashfs" ]]; then 243dcb3fd79SLei YU echo "Generating tarball to contain the SquashFS image and its MANIFEST" 244dcb3fd79SLei YU tar -cvf $outfile $files_to_sign $additional_files 245dcb3fd79SLei YU echo "SquashFSTarball at ${outfile}" 246dcb3fd79SLei YUelse 247dcb3fd79SLei YU tar -czvf $outfile $files_to_sign $additional_files 248dcb3fd79SLei YU echo "Static layout tarball at $outfile" 249dcb3fd79SLei YUfi 250dcb3fd79SLei YU 251