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