diff options
author | Stephen Cprek <smcprek@us.ibm.com> | 2016-10-25 17:10:06 -0500 |
---|---|---|
committer | Daniel M. Crowell <dcrowell@us.ibm.com> | 2016-11-16 13:24:54 -0500 |
commit | a301193c570cfb2462e7d3fe4e0a25b1fc24bef1 (patch) | |
tree | b40cc59af41b39c3f49693f597db8453873d33f3 /src | |
parent | 929569a227f8d3477fc47abf617e00111445dd4c (diff) | |
download | talos-hostboot-a301193c570cfb2462e7d3fe4e0a25b1fc24bef1.tar.gz talos-hostboot-a301193c570cfb2462e7d3fe4e0a25b1fc24bef1.zip |
Port new buildpnor files from p8
Change-Id: I47ab0092b591a0c995c139b9abbfa3acac4b46b2
RTC: 163081
Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/31819
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Reviewed-by: Michael Baiocchi <mbaiocch@us.ibm.com>
Reviewed-by: Nicholas E. Bofferding <bofferdn@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/build/buildpnor/PnorUtils.pm | 505 | ||||
-rwxr-xr-x | src/build/buildpnor/genPnorImages.pl | 1140 | ||||
-rwxr-xr-x | src/build/buildpnor/parse-pnor | 197 | ||||
-rwxr-xr-x | src/build/tools/parse-pnor | 385 |
4 files changed, 1842 insertions, 385 deletions
diff --git a/src/build/buildpnor/PnorUtils.pm b/src/build/buildpnor/PnorUtils.pm new file mode 100644 index 000000000..4c167d5c0 --- /dev/null +++ b/src/build/buildpnor/PnorUtils.pm @@ -0,0 +1,505 @@ +#!/usr/bin/perl +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/build/buildpnor/PnorUtils.pm $ +# +# OpenPOWER HostBoot Project +# +# Contributors Listed Below - COPYRIGHT 2016 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG + +package PnorUtils; + +use File::Basename; +use Exporter 'import'; +@EXPORT_OK = qw(loadPnorLayout getNumber traceErr trace run_command PAGE_SIZE + loadBinFiles findLayoutKeyByEyeCatch checkSpaceConstraints + getSwSignatures getBinDataFromFile checkFile); +use strict; + +my $TRAC_ERR = 0; +# 0=errors, >0 for more traces, leaving at 1 to keep key milestone traces. +my $g_trace = 1; + +use XML::Simple; +################################################################################ +# Set PREFERRED_PARSER to XML::Parser. Otherwise it uses XML::SAX which contains +# bugs that result in XML parse errors that can be fixed by adjusting white- +# space (i.e. parse errors that do not make sense). +################################################################################ +$XML::Simple::PREFERRED_PARSER = 'XML::Parser'; + +use constant PAGE_SIZE => 4096; + +################################################################################ +# loadPnorLayout +################################################################################ +sub loadPnorLayout +{ + my ($i_pnorFile, $i_pnorLayoutRef, $i_physicalOffsets, $i_testRun) = @_; + my $this_func = (caller(0))[3]; + + unless(-e $i_pnorFile) + { + traceErr("$this_func: File not found: $i_pnorFile"); + return -1; + } + + #parse the input XML file + my $xs = new XML::Simple(keyattr=>[], forcearray => 1); + my $xml = $xs->XMLin($i_pnorFile); + + #Iterate over the <section> elements. + foreach my $sectionEl (@{$xml->{section}}) + { + my $description = $sectionEl->{description}[0]; + my $eyeCatch = $sectionEl->{eyeCatch}[0]; + my $physicalOffset = $sectionEl->{physicalOffset}[0]; + my $physicalRegionSize = $sectionEl->{physicalRegionSize}[0]; + my $side = $sectionEl->{side}[0]; + my $testonly = $sectionEl->{testonly}[0]; + my $ecc = (exists $sectionEl->{ecc} ? "yes" : "no"); + my $sha512Version = (exists $sectionEl->{sha512Version} ? "yes" : "no"); + my $sha512perEC = (exists $sectionEl->{sha512perEC} ? "yes" : "no"); + my $preserved = (exists $sectionEl->{preserved} ? "yes" : "no"); + my $reprovision = (exists $sectionEl->{reprovision} ? "yes" : "no"); + my $readOnly = (exists $sectionEl->{readOnly} ? "yes" : "no"); + my $xz = ""; + my $xzSize = 0; + if((exists $sectionEl->{compressed}) && + ($sectionEl->{compressed}[0]->{algorithm}[0] eq "xz")) + { + $xz = "xz"; + $xzSize = $sectionEl->{compressed}[0]->{uncompressedSize}[0]; + } + if (($i_testRun == 0) && ($sectionEl->{testonly}[0] eq "yes")) + { + next; + } + + trace(3, "$this_func: description = $description, eyeCatch=$eyeCatch, physicalOffset = $physicalOffset, physicalRegionSize=$physicalRegionSize, side=$side"); + + $physicalOffset = getNumber($physicalOffset); + $physicalRegionSize = getNumber($physicalRegionSize); + + $$i_pnorLayoutRef{sections}{$physicalOffset}{description} = $description; + $$i_pnorLayoutRef{sections}{$physicalOffset}{eyeCatch} = $eyeCatch; + $$i_pnorLayoutRef{sections}{$physicalOffset}{physicalOffset} = $physicalOffset; + $$i_pnorLayoutRef{sections}{$physicalOffset}{physicalRegionSize} = $physicalRegionSize; + $$i_pnorLayoutRef{sections}{$physicalOffset}{side} = $side; + $$i_pnorLayoutRef{sections}{$physicalOffset}{ecc} = $ecc; + $$i_pnorLayoutRef{sections}{$physicalOffset}{sha512Version} = $sha512Version; + $$i_pnorLayoutRef{sections}{$physicalOffset}{sha512perEC} = $sha512perEC; + $$i_pnorLayoutRef{sections}{$physicalOffset}{preserved} = $preserved; + $$i_pnorLayoutRef{sections}{$physicalOffset}{reprovision} = $reprovision; + $$i_pnorLayoutRef{sections}{$physicalOffset}{readOnly} = $readOnly; + $$i_pnorLayoutRef{sections}{$physicalOffset}{compressed}{algorithm} = $xz; + $$i_pnorLayoutRef{sections}{$physicalOffset}{compressed}{uncompressedSize} = $xzSize; + + #store the physical offsets of each section in a hash, so, it is easy + #to search physicalOffsets based on the name of the section (eyecatch) + if ($side eq "sideless") + { + foreach my $metadata (@{$xml->{metadata}}) + { + foreach my $sides (@{$metadata->{side}}) + { + $$i_physicalOffsets{side}{$sides->{id}[0]}{eyecatch}{$eyeCatch} = $physicalOffset; + } + } + } + else + { + $$i_physicalOffsets{side}{$side}{eyecatch}{$eyeCatch} = $physicalOffset; + } + } + # Save the metadata - imageSize, blockSize, toc Information etc. + foreach my $metadataEl (@{$xml->{metadata}}) + { + # Get meta data + my $imageSize = $metadataEl->{imageSize}[0]; + my $blockSize = $metadataEl->{blockSize}[0]; + my $tocSize = $metadataEl->{tocSize}[0]; + my $arrangement = $metadataEl->{arrangement}[0]; + $imageSize = getNumber($imageSize); + $blockSize = getNumber($blockSize); + $tocSize = getNumber($tocSize); + $$i_pnorLayoutRef{metadata}{imageSize} = $imageSize; + $$i_pnorLayoutRef{metadata}{blockSize} = $blockSize; + $$i_pnorLayoutRef{metadata}{tocSize} = $tocSize; + $$i_pnorLayoutRef{metadata}{arrangement} = $arrangement; + + my $numOfSides = scalar (@{$metadataEl->{side}}); + my $sideSize = ($imageSize)/($numOfSides); + + trace(1, " $this_func: metadata: imageSize = $imageSize, blockSize=$blockSize, arrangement = $arrangement, numOfSides: $numOfSides, sideSize: $sideSize, tocSize: $tocSize"); + + #determine the TOC offsets from the arrangement and side Information + #stored in the layout xml + # + #Arrangement A-B-D means that the layout had Primary TOC (A), then backup TOC (B), then Data (pnor section information). + #Similaryly, arrangement A-D-B means that primary toc is followed by the data (section information) and then + #the backup TOC. + if ($arrangement eq "A-B-D") + { + my $count = 0; + foreach my $side (@{$metadataEl->{side}}) + { + my $golden = (exists $side->{golden} ? "yes" : "no"); + my $sideId = $side->{id}[0]; + my $primaryTOC = ($sideSize)*($count); + my $backupTOC = ($primaryTOC)+($tocSize); + + $$i_pnorLayoutRef{metadata}{sides}{$sideId}{toc}{primary} = $primaryTOC; + $$i_pnorLayoutRef{metadata}{sides}{$sideId}{toc}{backup} = $backupTOC; + $$i_pnorLayoutRef{metadata}{sides}{$sideId}{golden} = $golden; + + $count = $count + 1; + trace(1, "A-B-D: side:$sideId primaryTOC:$primaryTOC, backupTOC:$backupTOC, golden: $golden"); + } + } + elsif ($arrangement eq "A-D-B") + { + foreach my $side (@{$metadataEl->{side}}) + { + my $golden = (exists $side->{golden} ? "yes" : "no"); + my $sideId = $side->{id}[0]; + my $hbbAddr = $$i_physicalOffsets{side}{$sideId}{eyecatch}{"HBB"}; + my $primaryTOC = align_down($hbbAddr, $sideSize); + my $backupTOC = align_up($hbbAddr, $sideSize) - $tocSize; + + $$i_pnorLayoutRef{metadata}{sides}{$sideId}{toc}{primary} = $primaryTOC; + $$i_pnorLayoutRef{metadata}{sides}{$sideId}{toc}{backup} = $backupTOC; + $$i_pnorLayoutRef{metadata}{sides}{$sideId}{golden} = $golden; + trace(1, "A-D-B: side:$sideId HBB:$hbbAddr, primaryTOC:$primaryTOC, backupTOC:$backupTOC, golden: $golden"); + } + } + else + { + trace(0, "Arrangement:$arrangement is not supported"); + exit(1); + } + } + return 0; +} + +################################################################################ +# align_down: Align the input to the lower end of the PNOR side +################################################################################ +sub align_down +{ + my ($addr,$n) = @_; + return (($addr) - ($addr)%($n)); +} + +################################################################################ +# align_up: Align the input address to the higher end of the PNOR side +################################################################################ +sub align_up +{ + my ($addr,$n) = @_; + return ((($addr) + ($n-1)) & ~($n-1)); +} + +################################################################################ +# getNumber - handle hex or decimal input string +################################################################################ +sub getNumber +{ + my $inVal = shift; + if($inVal =~ "0x") + { + return oct($inVal); + } + else + { + return $inVal; + } +} + +################################################################################ +# trace +################################################################################ +sub traceErr +{ + my $i_string = shift; + trace($TRAC_ERR, $i_string); +} + +################################################################################ +# trace +################################################################################ +sub trace +{ + my $i_traceLevel; + my $i_string; + + ($i_traceLevel, $i_string) = @_; + + #traceLevel 0 is for errors + if($i_traceLevel == 0) + { + print "ERROR: ".$i_string."\n"; + } + elsif ($g_trace >= $i_traceLevel) + { + print "TRACE: ".$i_string."\n"; + } +} + +################################################################################ +# run_command - First print, and then run a system command, erroring out if the +# command does not complete successfully +################################################################################ +sub run_command +{ + my $command = shift; + trace(1, "$command"); + my $rc = system($command); + die "Error running command: $command. Nonzero return code of ($rc) returned.\n" if ($rc !=0); +} + +################################################################################ +# loadBinFiles - Load bin file CSV into hash +################################################################################ +sub loadBinFiles +{ + my ($i_binFilesCSV, $i_binFilesRef) = @_; + + print "Loading bin files...\n"; + + foreach my $binFile (split(',',$i_binFilesCSV)) + { + # Format is 'BIN_NAME=FILENAME' + my @arr = split('=', $binFile); + + $$i_binFilesRef{$arr[0]} = $arr[1]; + } +} + +################################################################################ +# findLayoutKeyByEyeCatch - Figure out hash key based on eyeCatcher +################################################################################ +sub findLayoutKeyByEyeCatch +{ + my $layoutKey = -1; + my($eyeCatch, $i_pnorLayoutRef) = @_; + my $key; + + my %sectionHash = %{$$i_pnorLayoutRef{sections}}; + for $key ( keys %sectionHash) + { + if($sectionHash{$key}{eyeCatch} eq $eyeCatch) + { + $layoutKey = $key; + last; + } + } + + return $layoutKey; +} + +################################################################################ +# checkSpaceConstraints - Make sure provided files will fit in their sections +################################################################################ +sub checkSpaceConstraints +{ + my ($i_pnorLayoutRef, $i_binFiles, $testRun) = @_; + my $this_func = (caller(0))[3]; + my $key; + + my %sectionHash = %{$$i_pnorLayoutRef{sections}}; + + for $key ( keys %{$i_binFiles}) + { + my $filesize = -s $$i_binFiles{$key}; + + my $layoutKey = findLayoutKeyByEyeCatch($key, \%$i_pnorLayoutRef); + if( $layoutKey == -1) + { + die "ERROR: $this_func: entry not found in PNOR layout for file $$i_binFiles{$key}, under eyecatcher $key"; + } + + my $eyeCatch = $sectionHash{$layoutKey}{eyeCatch}; + my $physicalRegionSize = $sectionHash{$layoutKey}{physicalRegionSize}; + + if($filesize > $physicalRegionSize) + { + # If this is a test run increase HBI size by PAGE_SIZE until all test + # cases fit + if ($testRun && $eyeCatch eq "HBI") + { + print "Adjusting HBI size - ran out of space for test cases\n"; + my $hbbKey = findLayoutKeyByEyeCatch("HBB", \%$i_pnorLayoutRef); + adjustHbiPhysSize(\%sectionHash, $layoutKey, $filesize, $hbbKey); + } + else + { + die "ERROR: $this_func: Image provided ($$i_binFiles{$eyeCatch}) has size ($filesize) which is greater than allocated space ($physicalRegionSize) for section=$eyeCatch. Aborting!"; + } + } + } + trace(1, "Done checkSpaceConstraints"); +} + + +############################################################################### +# adjustHbiPhysSize - Adjust HBI physical size when running test cases and fix +# up physical offsets of partitions between HBI and HBB +################################################################################ +sub adjustHbiPhysSize +{ + my ($i_sectionHashRef, $i_hbiKey, $i_filesize, $i_hbbKey) = @_; + + my %sectionHash = %$i_sectionHashRef; + + # Increment HBI physical size by PAGE_SIZE until the HBI file can fit + my $hbi_old = $sectionHash{$i_hbiKey}{physicalRegionSize}; + while ($i_filesize > $sectionHash{$i_hbiKey}{physicalRegionSize}) + { + $sectionHash{$i_hbiKey}{physicalRegionSize} += PAGE_SIZE; + } + my $hbi_move = $sectionHash{$i_hbiKey}{physicalRegionSize} - $hbi_old; + my $hbi_end = $sectionHash{$i_hbiKey}{physicalRegionSize} + $hbi_move; + + # Fix up physical offset affected by HBI size change + foreach my $section (keys %sectionHash) + { + # Only fix partitions after HBI and before HBB + if ( ( $sectionHash{$section}{physicalOffset} > + $sectionHash{$i_hbiKey}{physicalOffset} ) && + ( $sectionHash{$section}{physicalOffset} < + $sectionHash{$i_hbbKey}{physicalOffset} ) + ) + { + $sectionHash{$section}{physicalOffset} += $hbi_move; + # Ensure section adjustment does not cause an overlap with HBB + if ($sectionHash{$section}{physicalOffset} > + $sectionHash{$i_hbbKey}{physicalOffset}) + { + die "Error detected $sectionHash{$section}{eyeCatch}'s adjusted size overlaps HBB"; + } + } + } +} + +############################################################################### +# getSwSignatures - Extracts concatenation of sw signatures from secure +# container header. Simplified to skip around to the data +# needed. +################################################################################ +sub getSwSignatures +{ + my ($i_file) = @_; + + # Constants defined in ROM code. + use constant ecid_size => 16; #bytes + use constant sw_key_size => 132; #bytes + + # Offsets defined in secure boot PLDD. + # Relative offset are based on the previous constant + use constant sw_key_count_offset => 450; #bytes + use constant relative_offset_to_hw_ecid_count => 73; #bytes + # Offset assuming Default of ECID count = 0 and SW count = 1 + use constant relative_offset_to_sw_ecid_count => 626; #bytes + # Offset assuming Default of ECID count = 0 + use constant relative_offset_to_sw_signatures => 1; #bytes + + # Header info + my $sw_key_count = 0; + my $hw_ecid_count = 0; + my $sw_ecid_count = 0; + my $sw_signatures = 0; + + # Get header data from file + my $header_data = getBinDataFromFile($i_file); + + # get sw key count + my $cur_offset = sw_key_count_offset; + $sw_key_count = unpack("x$cur_offset C", $header_data); + + # get hw ecid counts + $cur_offset += relative_offset_to_hw_ecid_count; + $hw_ecid_count = unpack("x$cur_offset C", $header_data); + + # Variable size elements of a secure header + # Note 1 sw_key is already considered in above constants + my $num_optional_keys = ($sw_key_count > 1) ? + ($sw_key_count - 1) : 0; + my $variable_size_offset = ($num_optional_keys * sw_key_size) + + ($hw_ecid_count * ecid_size); + + # get sw ecid count + $cur_offset += relative_offset_to_sw_ecid_count + $variable_size_offset; + $sw_ecid_count = unpack("x$cur_offset C", $header_data); + + # Variable size elements of a secure header + $variable_size_offset = ($sw_ecid_count * ecid_size); + + # get sw signatures + $cur_offset += relative_offset_to_sw_signatures + $variable_size_offset; + # Get concatenation of all possible sw signatures + $sw_signatures = substr($header_data, $cur_offset, + $sw_key_count*sw_key_size); + + return $sw_signatures; +} + +############################################################################### +# getBinDataFromFile - Extracts binary data from a given file into a variable +################################################################################ +sub getBinDataFromFile +{ + my ($i_file) = @_; + + my $data = 0; + open (BINFILE, "<", $i_file) or die "Error opening file $i_file: $!\n"; + binmode BINFILE; + read(BINFILE,$data,PAGE_SIZE); + die "Error reading of $i_file failed" if $!; + close(BINFILE); + die "Error closing $i_file failed" if $!; + + return $data; +} + +# sub checkFile +# +# Check if file exists and is of type XML +# +# @param [in] i_layoutFile - PNOR layout file +# @return - N/A Die on failure +# +sub checkFile +{ + my $i_layoutFile = shift; + + my($filename, $dirs, $suffix) = fileparse($i_layoutFile,".xml"); + + unless(-e $i_layoutFile) + { + die "File not found: $i_layoutFile"; + } + if ($suffix ne ".xml") + { + die "File not type XML: $i_layoutFile"; + } +} + +1; diff --git a/src/build/buildpnor/genPnorImages.pl b/src/build/buildpnor/genPnorImages.pl new file mode 100755 index 000000000..2ac721bbe --- /dev/null +++ b/src/build/buildpnor/genPnorImages.pl @@ -0,0 +1,1140 @@ +#!/usr/bin/perl +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/build/buildpnor/genPnorImages.pl $ +# +# OpenPOWER HostBoot Project +# +# Contributors Listed Below - COPYRIGHT 2016 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG + +use strict; +use Data::Dumper; +use File::Basename; +use Cwd qw(abs_path cwd); +use lib dirname abs_path($0); +use PnorUtils qw(loadPnorLayout getNumber traceErr trace run_command PAGE_SIZE + loadBinFiles findLayoutKeyByEyeCatch checkSpaceConstraints + getSwSignatures getBinDataFromFile); +use Getopt::Long qw(:config pass_through); + +use constant COMMUNITY => "community"; + +# Hostboot base image constants for the hardware header portion of the +# secureboot header +use constant BASE_IMAGE_TOTAL_CONTAINER_SIZE => 0x000000000007EF80; +use constant BASE_IMAGE_TARGET_HRMOR => 0x0000000008000000; +use constant BASE_IMAGE_INSTRUCTION_START_STACK_POINTER => 0x0000000008280000; + +################################################################################ +# Be explicit with POSIX +# Everything is exported by default (with a handful of exceptions). This is an +# unfortunate backwards compatibility feature and its use is strongly discouraged. +# You should either prevent the exporting (by saying use POSIX (); , as usual) +# and then use fully qualified names (e.g. POSIX::SEEK_END ), or give an explicit +# import list. If you do neither and opt for the default (as in use POSIX; ), +# you will import hundreds and hundreds of symbols into your namespace. +################################################################################ +use POSIX (); +use Digest::SHA qw(sha512); + +my $programName = File::Basename::basename $0; +my @systemBinFiles = (); +my %pnorLayout = (); +my %PhysicalOffsets = (); + +# Truncate SHA to n bytes +use constant SHA_TRUNCATE_SIZE => 32; +# Defined in src/include/sys/vfs.h +use constant VFS_EXTENDED_MODULE_MAX => 128; +# VfsSystemModule struct size +use constant VFS_MODULE_TABLE_ENTRY_SIZE => 112; +# VFS Module table max size +use constant VFS_MODULE_TABLE_MAX_SIZE => VFS_EXTENDED_MODULE_MAX + * VFS_MODULE_TABLE_ENTRY_SIZE; + +# Flag parameter string passed into signing tools +# Note spaces before/after are critical. +use constant LOCAL_SIGNING_FLAG => " -flag "; +use constant OP_SIGNING_FLAG => " -flags "; +# Security bits HW flag strings +use constant OP_BUILD_FLAG => 0x80000000; +use constant FIPS_BUILD_FLAG => 0x40000000; +use constant KEY_TRANSITION_FLAG => 0x00000001; + +# TODO: RTC 163655 +# Implement dynamic support for choosing FSP or op-build flag type. +# For now, assume OP build +my $buildFlag = OP_BUILD_FLAG; + +# Corrupt parameter strings +my $CORRUPT_PROTECTED = "pro"; +my $CORRUPT_UNPROTECTED = "unpro"; +use constant MAX_PAGES_TO_CORRUPT => 10; +# rand file prefix string. Note hbDistribute cleans up files with this prefix +use constant RAND_PREFIX => "rand-"; + +# Signing modes +my $DEVELOPMENT = "development"; +my $IMPRINT = "imprint"; +my $PRODUCTION = "production"; + +################################################################################ +# I/O parsing +################################################################################ + +my %globals = (); +my $bin_dir = cwd(); +my $secureboot = 0; +my $testRun = 0; +my $pnorLayoutFile = ""; +my $system_target = ""; +my $build_all = 0; +my $install_all = 0; +my $key_transition = ""; +my $help = 0; +my %partitionsToCorrupt = (); +my $sign_mode = $DEVELOPMENT; +my $sb_signing_config_file = ""; + +GetOptions("binDir:s" => \$bin_dir, + "secureboot" => \$secureboot, + "test" => \$testRun, + "pnorLayout:s" => \$pnorLayoutFile, + "systemBinFiles:s" => \@systemBinFiles, + "build-all" => \$build_all, + "install-all" => \$install_all, + "key-transition:s" => \$key_transition, + "corrupt:s" => \%partitionsToCorrupt, + "sign-mode:s" => \$sign_mode, + "sb-signing-config-file:s" => \$sb_signing_config_file, + "help" => \$help); + +if ($help) +{ + usage(); + exit 0; +} + +################################################################################ +# Environment Setup, Checking, and Variable Initialization +################################################################################ + +# Put mode transition input into a hash and ensure a valid signing mode +my %signMode = ( $DEVELOPMENT => 1, + $PRODUCTION => 0 ); +if ($sign_mode =~ m/^$DEVELOPMENT/i) +{} +elsif ($sign_mode =~ m/^$PRODUCTION/i) +{ + $signMode{$PRODUCTION} = 1; + $signMode{$DEVELOPMENT} = 0; +} +else +{ + die "Invalid signing mode = $sign_mode"; +} + +# Secure boot signing config file only required in production mode. +if ($signMode{$PRODUCTION}) +{ + die "SB signing config file path not provided" if ($sb_signing_config_file eq ""); +} + +# Put key transition input into a hash and ensure a valid key transition mode +my %keyTransition = ( enabled => 0, + $IMPRINT => 0, + $PRODUCTION => 0 ); +if ($key_transition =~ m/^$IMPRINT/i) +{ + $keyTransition{$IMPRINT} = 1; + $keyTransition{enabled} = 1; +} +elsif ($key_transition =~ m/^$PRODUCTION/i) +{ + $keyTransition{$PRODUCTION} = 1; + $keyTransition{enabled} = 1; +} +elsif ($key_transition ne "") +{ + die "Invalid key transition mode = $key_transition"; +} + +if ($secureboot) +{ + # Ensure all values of partitionsToCorrupt hash are valid. + # Allow some flexibility for the user and do a regex, case insensitive check + # to properly clean up the corrupt partition hash. + foreach my $key (keys %partitionsToCorrupt) + { + my $value = $partitionsToCorrupt{$key}; + if ($value eq "" || $value =~ m/^$CORRUPT_PROTECTED/i) + { + $partitionsToCorrupt{uc($key)} = $CORRUPT_PROTECTED + } + elsif ($value =~ m/^$CORRUPT_UNPROTECTED/i) + { + $partitionsToCorrupt{uc($key)} = $CORRUPT_UNPROTECTED; + } + else + { + die "Error> Unsupported option for --corrupt, value \"$key=$value\""; + } + } +} + +# @TODO RTC: 155374 add official signing support including up to 3 sw keys +# Signing and Dev key directory location set via env vars +my $SIGNING_DIR = $ENV{'SIGNING_DIR'}; +my $DEV_KEY_DIR = $ENV{'DEV_KEY_DIR'}; + +if ($secureboot) +{ + # Check all components needed for developer signing + die "Signing Dir = $SIGNING_DIR DNE" if(! -d $SIGNING_DIR); + die "Dev Key Dir = $DEV_KEY_DIR DNE" if(! -d $DEV_KEY_DIR); + die "hw_key_a DNE in $DEV_KEY_DIR" if(!glob("$DEV_KEY_DIR/hw_key_a*")); + die "hw_key_b DNE in $DEV_KEY_DIR" if(!glob("$DEV_KEY_DIR/hw_key_b*")); + die "hw_key_c DNE in $DEV_KEY_DIR" if(!glob("$DEV_KEY_DIR/hw_key_c*")); + die "sw_key_a DNE in $DEV_KEY_DIR" if(!glob("$DEV_KEY_DIR/sw_key_a*")); +} + +my $openSigningTool = 0; +my $SIGNING_TOOL_EDITION = $ENV{'SIGNING_TOOL_EDITION'}; +if($SIGNING_TOOL_EDITION eq COMMUNITY) +{ + $openSigningTool = 1; +} + +### Local development signing +# Requires naming convention of hw/sw keys in DEV_KEY_DIR +my $SIGN_BUILD_PARAMS = "-skp ${DEV_KEY_DIR}/sw_key_a"; + +# Key prefix used for current key siging of partitions (N/A for open edition) +my $SIGN_PREFIX_PARAMS = "-hka ${DEV_KEY_DIR}/hw_key_a -hkb " + . "${DEV_KEY_DIR}/hw_key_b -hkc ${DEV_KEY_DIR}/hw_key_c " + . "-skp ${DEV_KEY_DIR}/sw_key_a"; + +# Key prefix used for secureboot key transition partition. +# Default key transition to same keys. +my $SIGN_SBKT_PREFIX_PARAMS = $SIGN_PREFIX_PARAMS; +if ($keyTransition{enabled}) +{ + # Note: simply reordered the keys to create a pseudo production key. + $SIGN_SBKT_PREFIX_PARAMS = "-hka ${DEV_KEY_DIR}/hw_key_c -hkb " + . "${DEV_KEY_DIR}/hw_key_b -hkc ${DEV_KEY_DIR}/hw_key_a " + . "-skp ${DEV_KEY_DIR}/sw_key_a"; +} + +### Open POWER signing +my $OPEN_SIGN_REQUEST="$SIGNING_DIR/crtSignedContainer.pl "; +# By default key transition container is unused +my $OPEN_SIGN_KEY_TRANS_REQUEST = $OPEN_SIGN_REQUEST; + +# Production signing parameters +my $OPEN_PRD_SIGN_PARAMS = "--mode production " + . " --sign-project-config $sb_signing_config_file"; +# Imprint key signing parameters +my $OPEN_DEV_SIGN_PARAMS = " -hwPrivKeyA $DEV_KEY_DIR/hw_key_a.key " + . "-hwPrivKeyB $DEV_KEY_DIR/hw_key_b.key " + . "-hwPrivKeyC $DEV_KEY_DIR/hw_key_c.key " + . "-swPrivKeyP $DEV_KEY_DIR/sw_key_a.key"; + +# Handle key transition and production signing logic +# If in production mode, key transition is not supported yet +# If in developement mode, key transition can move to either imprint or +# production keys +if ($signMode{$PRODUCTION}) +{ + # Production to Production key transition not supported yet + $OPEN_SIGN_REQUEST .= $OPEN_PRD_SIGN_PARAMS; + $OPEN_SIGN_KEY_TRANS_REQUEST = ""; +} +elsif ($keyTransition{enabled} && $signMode{$DEVELOPMENT}) +{ + $OPEN_SIGN_REQUEST .= $OPEN_DEV_SIGN_PARAMS; + if ($keyTransition{$IMPRINT}) + { + $OPEN_SIGN_KEY_TRANS_REQUEST .= $OPEN_DEV_SIGN_PARAMS; + } + elsif ($keyTransition{$PRODUCTION}) + { + $OPEN_SIGN_KEY_TRANS_REQUEST .= "$OPEN_PRD_SIGN_PARAMS --sign-project-FW-token SBKT"; + } +} +else +{ + $OPEN_SIGN_REQUEST .= $OPEN_DEV_SIGN_PARAMS; + $OPEN_SIGN_KEY_TRANS_REQUEST = ""; +} + +### Secureboot headers +# Contains the appropriate flags, prefix, and file names. +my $randPrefix = "rand-".POSIX::ceil(rand(0xFFFFFFFF)); +my %sb_hdrs = ( + DEFAULT => { + flags => sprintf("0x%08X",$buildFlag), + prefix => $SIGN_PREFIX_PARAMS, + file => "$bin_dir/$randPrefix.default.secureboot.hdr.bin" + }, + SBKT => { + outer => { + flags => sprintf("0x%08X", $buildFlag | KEY_TRANSITION_FLAG), + prefix => $SIGN_PREFIX_PARAMS, + file => "$bin_dir/$randPrefix.sbkt.outer.secureboot.hdr.bin" + }, + inner => { + flags => sprintf("0x%08X", $buildFlag), + prefix => $SIGN_SBKT_PREFIX_PARAMS, + file => "$bin_dir/$randPrefix.sbkt.inner.secureboot.hdr.bin" + } + } +); + +################################################################################ +# main +################################################################################ + +# Print all settings in one print statement to avoid parallel build to mess +# up output. +my $SETTINGS = "\n//========== Generate PNOR Image Settings ==========/\n"; +$SETTINGS .= $build_all ? "Build Phase = build_all\n" : ""; +$SETTINGS .= $install_all ? "Build Phase = install_all\n" : ""; +$SETTINGS .= $testRun ? "Test Mode = Yes\n" : "Test Mode = No\n"; +$SETTINGS .= $secureboot ? "Secureboot = Enabled\n" : "Secureboot = Disabled\n"; +$SETTINGS .= %partitionsToCorrupt && $secureboot ? "Corrupt Partitions: ".Dumper \%partitionsToCorrupt : ""; +$SETTINGS .= $secureboot ? "Sign Mode = $sign_mode\n" : "Sign Mode = NA\n"; +$SETTINGS .= $key_transition && $secureboot ? "Key Transition Mode = $key_transition\n" : "Key Transition Mode = NA\n"; +$SETTINGS .= "//====================================================//\n\n"; +print $SETTINGS; + +if($secureboot && !$openSigningTool) +{ + # Generate each secureboot header file + foreach my $header (keys %sb_hdrs) + { + next if($header eq "SBKT" && !$key_transition); + + # SBKT parition has 2 sections outer and inner, need to create both + if ($header eq "SBKT") + { + foreach my $section (keys %{$sb_hdrs{$header}}) + { + run_command("$SIGNING_DIR/prefix -good -of $sb_hdrs{$header}{$section}{file}". + LOCAL_SIGNING_FLAG."$sb_hdrs{$header}{$section}{flags}". + " $sb_hdrs{$header}{$section}{prefix}"); + } + } + else + { + run_command("$SIGNING_DIR/prefix -good -of $sb_hdrs{$header}{file}". + LOCAL_SIGNING_FLAG."$sb_hdrs{$header}{flags}". + " $sb_hdrs{$header}{prefix}"); + } + } + + # Generate test containers once and limit to build phase + if ($build_all) + { + gen_test_containers(); + } +} + +#Load PNOR Layout XML file +loadPnorLayout($pnorLayoutFile, \%pnorLayout, \%PhysicalOffsets, $testRun); + +# Generate final images for each system's bin files. +foreach my $binFilesCSV (@systemBinFiles) +{ + my %binFiles = (); + my $system_target = ""; + + # Check if format includes a system target 'TARGET:<csv of bin files>' + if ($binFilesCSV =~ m/:/) + { + my @arr = split(':', $binFilesCSV); + $system_target = $arr[0]; + $binFilesCSV = $arr[1]; + } + + # Load bin files into hash to know what to generate + loadBinFiles($binFilesCSV, \%binFiles); + + #Perform any data integrity manipulation (ECC, sha-hash, etc) + manipulateImages(\%pnorLayout, \%binFiles, $system_target); + + # Make sure provided files will fit in their sections + checkSpaceConstraints(\%pnorLayout, \%binFiles, $testRun); +} + + +# Clean up temp header files +foreach my $header (keys %sb_hdrs) +{ + if($header eq "SBKT") + { + foreach my $section (keys %{$sb_hdrs{$header}}) + { + system("rm -f $sb_hdrs{$header}{$section}{file}"); + die "Could not delete $sb_hdrs{$header}{$section}{file}" if $?; + } + } + else + { + system("rm -f $sb_hdrs{$header}{file}"); + die "Could not delete $sb_hdrs{$header}{file}" if $?; + } +} + +################################################################################ +# Subroutines +################################################################################ + +################################################################################ +# partitionDepSort +# Custom sort to ensure images are handled in the correct dependency order. +# If a dependency is not specified in the hash used, use default behavior. +################################################################################ +# Hardcoded defined order that binfiles should be handled. +my %partitionDeps = ( HBB => 0, + HBI => 1); +sub partitionDepSort +{ + # If $a exists but $b does not, set $a < $b + if (exists $partitionDeps{$a} && !exists $partitionDeps{$b}) + { + -1 + } + # If $a does not exists but $b does, set $a > $b + elsif (!exists $partitionDeps{$a} && exists $partitionDeps{$b}) + { + 1 + } + # If both $a and $b exist, actually compare values. + elsif (exists $partitionDeps{$a} && exists $partitionDeps{$b}) + { + if ($partitionDeps{$a} < $partitionDeps{$b}) {-1} + elsif ($partitionDeps{$a} > $partitionDeps{$b}) {1} + else {0} + } + # If neither $a or $b have a dependency, order doesn't matter + else {0} +} + +################################################################################ +# manipulateImages - Perform any ECC/padding/sha/signing manipulations +################################################################################ +sub manipulateImages +{ + my ($i_pnorLayoutRef, $i_binFilesRef, $system_target) = @_; + my $this_func = (caller(0))[3]; + + my %sectionHash = %{$$i_pnorLayoutRef{sections}}; + trace(1, "manipulateImages"); + + # Prefix for temporary files for parallel builds + my $parallelPrefix = RAND_PREFIX.POSIX::ceil(rand(0xFFFFFFFF)).$system_target; + + # Partitions that have a hash page table at the beginning of the section + # for secureboot purposes. + my %hashPageTablePartitions = (HBI => 1); + + my %preReqImages = ( + HBB_SW_SIG_FILE => "$bin_dir/$parallelPrefix.hbb_sw_sig.bin" + ); + + foreach my $key (sort partitionDepSort keys %{$i_binFilesRef}) + { + my %callerHwHdrFields = ( + configure => 0, + totalContainerSize => 0, + targetHrmor => 0, + instructionStartStackPointer => 0); + + my $layoutKey = findLayoutKeyByEyeCatch($key, \%$i_pnorLayoutRef); + my $eyeCatch = $sectionHash{$layoutKey}{eyeCatch}; + my %tempImages = ( + HDR_PHASE => "$bin_dir/$parallelPrefix.$eyeCatch.temp.hdr.bin", + PREFIX_PHASE => "$bin_dir/$parallelPrefix.$eyeCatch.temp.hdr.prefix.bin", + TEMP_SHA_IMG => "$bin_dir/$parallelPrefix.$eyeCatch.temp.sha.bin", + PAD_PHASE => "$bin_dir/$parallelPrefix.$eyeCatch.temp.pad.bin", + ECC_PHASE => "$bin_dir/$parallelPrefix.$eyeCatch.temp.bin.ecc", + VFS_MODULE_TABLE => => "$bin_dir/$parallelPrefix.$eyeCatch.vfs_module_table.bin", + TEMP_BIN => "$bin_dir/$parallelPrefix.$eyeCatch.temp.bin", + PAYLOAD_TEXT => "$bin_dir/$parallelPrefix.$eyeCatch.payload_text.bin", + PROTECTED_PAYLOAD => "$bin_dir/$parallelPrefix.$eyeCatch.protected_payload.bin" + ); + + my $size = $sectionHash{$layoutKey}{physicalRegionSize}; + my $bin_file = $$i_binFilesRef{$eyeCatch}; + # Check if bin file is system specific and prefix target to the front + my $final_bin_file = ($system_target eq "")? "$bin_dir/$eyeCatch.bin": + "$bin_dir/$system_target.$eyeCatch.bin"; + + # Get size of partition without ecc + if ($sectionHash{$layoutKey}{ecc} eq "yes") + { + $size = page_aligned_size_wo_ecc($size); + } + + # Sections that have secureboot support. Secureboot still must be + # enabled for secureboot actions on these partitions to occur. + my $isNormalSecure = ($eyeCatch eq "SBE") + || ($eyeCatch eq "SBEC") + || ($eyeCatch eq "PAYLOAD") + || ($eyeCatch eq "SBKT") + || ($eyeCatch eq "OCC") + || ($eyeCatch eq "HBRT") + || ($eyeCatch eq "CAPP") + || ($eyeCatch eq "BOOTKERNEL"); + + my $isSpecialSecure = ($eyeCatch eq "HBB") + || ($eyeCatch eq "HBI") + || ($eyeCatch eq "HBD"); + + my $openSigningFlags = OP_SIGNING_FLAG.$sb_hdrs{DEFAULT}{flags}; + my $secureboot_hdr = $sb_hdrs{DEFAULT}{file}; + + my $CUR_OPEN_SIGN_REQUEST = "$OPEN_SIGN_REQUEST $openSigningFlags"; + if ($signMode{$PRODUCTION}) + { + $CUR_OPEN_SIGN_REQUEST .= " --sign-project-FW-token $eyeCatch "; + } + + # Used for corrupting partitions. By default all protected offsets start + # immediately after the container header which is size = PAGE_SIZE. + # *Note: this is before ECC. + my $protectedOffset = PAGE_SIZE; + + # Handle partitions that have an input binary. + if (-e $bin_file) + { + # FSP workaround to keep original bin names + my $fsp_file = $bin_file; + my $fsp_prefix = ""; + + # Header Phase + if( ($sectionHash{$layoutKey}{sha512Version} eq "yes") + || ($secureboot && $isSpecialSecure) ) + { + $fsp_prefix.=".header"; + # Add secure container header + # @TODO RTC:155374 Remove when official signing supported + if ($secureboot) + { + $callerHwHdrFields{configure} = 1; + if (exists $hashPageTablePartitions{$eyeCatch}) + { + if ($eyeCatch eq "HBI") + { + # Pass HBB sw signatures as the salt entry. + $tempImages{hashPageTable} = genHashPageTable($bin_file, $eyeCatch, + getBinDataFromFile($preReqImages{HBB_SW_SIG_FILE})); + } + else + { + $tempImages{hashPageTable} = genHashPageTable($bin_file, $eyeCatch); + } + } + # Add hash page table + if ($tempImages{hashPageTable} ne "" && -e $tempImages{hashPageTable}) + { + trace(1,"Adding hash page table for $eyeCatch"); + my $hashPageTableSize = -s $tempImages{hashPageTable}; + die "hashPageTable size undefined: errno = $!" unless(defined $hashPageTableSize); + # Move protected offset after hash page table. + $protectedOffset += $hashPageTableSize; + if ($eyeCatch eq "HBI") + { + # Add the VFS module table to the payload text section. + run_command("dd if=$bin_file of=$tempImages{VFS_MODULE_TABLE} count=".VFS_EXTENDED_MODULE_MAX." ibs=".VFS_MODULE_TABLE_ENTRY_SIZE); + # Remove VFS module table from bin file + run_command("dd if=$bin_file of=$tempImages{TEMP_BIN} skip=".VFS_EXTENDED_MODULE_MAX." ibs=".VFS_MODULE_TABLE_ENTRY_SIZE); + run_command("cp $tempImages{TEMP_BIN} $bin_file"); + # Pad after hash page table to have the VFS module table end at a 4K boundary + my $padSize = PAGE_SIZE - (($hashPageTableSize + VFS_MODULE_TABLE_MAX_SIZE) % PAGE_SIZE); + run_command("dd if=/dev/zero bs=$padSize count=1 | tr \"\\000\" \"\\377\" >> $tempImages{hashPageTable} "); + + # Move protected offset after padding of hash page table. + $protectedOffset += $padSize; + + # Payload text section + run_command("cat $tempImages{hashPageTable} $tempImages{VFS_MODULE_TABLE} > $tempImages{PAYLOAD_TEXT} "); + } + else + { + run_command("cp $tempImages{hashPageTable} $tempImages{PAYLOAD_TEXT}"); + } + + if($openSigningTool) + { + run_command("$CUR_OPEN_SIGN_REQUEST " + . "-protectedPayload $tempImages{PAYLOAD_TEXT} " + . "-out $tempImages{PROTECTED_PAYLOAD}"); + } + else + { + # @TODO RTC:155374 Remove when official signing + # supported + run_command("$SIGNING_DIR/build -good -if $secureboot_hdr -of $tempImages{PROTECTED_PAYLOAD} -bin $tempImages{PAYLOAD_TEXT} $SIGN_BUILD_PARAMS"); + } + + run_command("cat $tempImages{PROTECTED_PAYLOAD} $bin_file > $tempImages{HDR_PHASE}"); + } + # Handle read-only protected payload + elsif ($eyeCatch eq "HBD") + { + if($openSigningTool) + { + run_command("$CUR_OPEN_SIGN_REQUEST " + . "-protectedPayload $bin_file.protected " + . "-out $tempImages{PROTECTED_PAYLOAD}"); + } + else + { + # @TODO RTC:155374 Remove when official signing + # supported + run_command("$SIGNING_DIR/build -good -if $secureboot_hdr -of $tempImages{PROTECTED_PAYLOAD} -bin $bin_file.protected $SIGN_BUILD_PARAMS"); + } + + run_command("cat $tempImages{PROTECTED_PAYLOAD} $bin_file.unprotected > $tempImages{HDR_PHASE}"); + } + else + { + if($openSigningTool) + { + my $codeStartOffset = ($eyeCatch eq "HBB") ? + "-code-start-offset 0x00000180" : ""; + run_command("$CUR_OPEN_SIGN_REQUEST " + . "$codeStartOffset " + . "-protectedPayload $bin_file " + . "-out $tempImages{HDR_PHASE}"); + } + else + { + # @TODO RTC:155374 Remove when official signing + # supported + run_command("$SIGNING_DIR/build -good -if $secureboot_hdr -of $tempImages{HDR_PHASE} -bin $bin_file $SIGN_BUILD_PARAMS"); + } + } + + # Customize secureboot prefix header with container size, + # target HRMOR, and stack address (8 bytes each), in that + # order. Customization begins at offset 6 into the container + # header. + if($eyeCatch eq "HBB") + { + $callerHwHdrFields{totalContainerSize} + = BASE_IMAGE_TOTAL_CONTAINER_SIZE; + $callerHwHdrFields{targetHrmor} + = BASE_IMAGE_TARGET_HRMOR; + $callerHwHdrFields{instructionStartStackPointer} + = BASE_IMAGE_INSTRUCTION_START_STACK_POINTER; + # Save off HBB sw signatures for use by HBI + open (HBB_SW_SIG_FILE, ">", + $preReqImages{HBB_SW_SIG_FILE}) or die "Error opening file $preReqImages{HBB_SW_SIG_FILE}: $!\n"; + binmode HBB_SW_SIG_FILE; + print HBB_SW_SIG_FILE getSwSignatures($tempImages{HDR_PHASE}); + die "Error writing to $preReqImages{HBB_SW_SIG_FILE} failed" if $!; + close HBB_SW_SIG_FILE; + die "Error closing of $preReqImages{HBB_SW_SIG_FILE} failed" if $!; + } + } + # Add simiple version header + else + { + run_command("env echo -en VERSION\\\\0 > $tempImages{TEMP_SHA_IMG}"); + run_command("sha512sum $bin_file | awk \'{print \$1}\' | xxd -pr -r >> $tempImages{TEMP_SHA_IMG}"); + run_command("dd if=$tempImages{TEMP_SHA_IMG} of=$tempImages{HDR_PHASE} ibs=4k conv=sync"); + run_command("cat $bin_file >> $tempImages{HDR_PHASE}"); + } + } + elsif($secureboot + && ( ($sectionHash{$layoutKey}{sha512perEC} eq "yes") + || ($isNormalSecure))) + { + $callerHwHdrFields{configure} = 1; + if($openSigningTool) + { + run_command("$CUR_OPEN_SIGN_REQUEST " + . "-protectedPayload $bin_file " + . "-out $tempImages{HDR_PHASE}"); + } + else + { + # @TODO RTC:155374 Remove when official signing supported + run_command("$SIGNING_DIR/build -good -if $secureboot_hdr -of $tempImages{HDR_PHASE} -bin $bin_file $SIGN_BUILD_PARAMS"); + } + } + else + { + run_command("cp $bin_file $tempImages{HDR_PHASE}"); + } + + setCallerHwHdrFields(\%callerHwHdrFields, $tempImages{HDR_PHASE}); + + # Prefix phase + # Add SBE header to HBB + if($eyeCatch eq "HBB") + { + run_command("echo \"00000000001800000000000008000000000000000007EF80\" | xxd -r -ps - $tempImages{PREFIX_PHASE}"); + run_command("cat $tempImages{HDR_PHASE} >> $tempImages{PREFIX_PHASE}"); + } + # Otherwise propagate image to next phase + else + { + run_command("mv $tempImages{HDR_PHASE} $tempImages{PREFIX_PHASE}"); + } + + # Padding Phase + if ($eyeCatch eq "HBI" && $testRun) + { + # If "--test" flag set do not pad as the test HBI images is + # possibly larger than partition size and does not need to be + # fully padded. Size adjustments made in checkSpaceConstraints + run_command("dd if=$tempImages{PREFIX_PHASE} of=$tempImages{PAD_PHASE} ibs=4k conv=sync"); + } + else + { + run_command("dd if=$tempImages{PREFIX_PHASE} of=$tempImages{PAD_PHASE} ibs=$size conv=sync"); + } + + # Create .header.bin file for FSP + if ($build_all) + { + $fsp_file =~ s/\.bin/$fsp_prefix.bin/; + run_command("cp $tempImages{PAD_PHASE} $fsp_file"); + } + + # Corrupt section if user specified to do so, before ECC injection. + if ($secureboot && exists $partitionsToCorrupt{$eyeCatch}) + { + # If no protected file ($tempImages{PAYLOAD_TEXT}) exists + # for this partition, then that means there is no unprotected + # section. A protected file is only created when there's a need + # to split up the partition for signing purposes. + corrupt_partition($eyeCatch, $protectedOffset, + $tempImages{PAYLOAD_TEXT}, + $tempImages{PAD_PHASE}); + } + } + # Handle partitions that have no input binary. Simply zero or random + # fill the partition. + elsif (!-e $bin_file) + { + # Test partitions have random data + if ($eyeCatch eq "TEST" || $eyeCatch eq "TESTRO") + { + run_command("dd if=/dev/urandom of=$tempImages{PAD_PHASE} count=1 bs=$size"); + } + elsif ($eyeCatch eq "SBKT" && $secureboot && $keyTransition{enabled}) + { + $callerHwHdrFields{configure} = 1; + create_sb_key_transition_container($tempImages{PAD_PHASE}); + setCallerHwHdrFields(\%callerHwHdrFields, $tempImages{PAD_PHASE}); + } + # Other partitions fill with FF's if no empty bin file provided + else + { + run_command("dd if=/dev/zero bs=$size count=1 | tr \"\\000\" \"\\377\" > $tempImages{PAD_PHASE}"); + + # Add secure container header + if ($secureboot && $isNormalSecure && $eyeCatch ne "SBKT") + { + $callerHwHdrFields{configure} = 1; + # Remove PAGE_SIZE bytes from generated dummy content of file + # to make room for the secure header + my $fileSize = (-s $tempImages{PAD_PHASE}) - PAGE_SIZE; + die "fileSize undefined: errno = $!" unless(defined $fileSize); + run_command("dd if=$tempImages{PAD_PHASE} of=$tempImages{TEMP_BIN} count=1 bs=$fileSize"); + # @TODO RTC:155374 Remove when official signing supported + run_command("$SIGNING_DIR/build -good -if $secureboot_hdr -of $tempImages{PAD_PHASE} -bin $tempImages{TEMP_BIN} $SIGN_BUILD_PARAMS"); + setCallerHwHdrFields(\%callerHwHdrFields, $tempImages{PAD_PHASE}); + } + } + } + + # ECC Phase + if( ($sectionHash{$layoutKey}{ecc} eq "yes") ) + { + run_command("ecc --inject $tempImages{PAD_PHASE} --output $tempImages{ECC_PHASE} --p8"); + } + else + { + run_command("cp $tempImages{PAD_PHASE} $tempImages{ECC_PHASE}"); + } + + # Compression phase + if( ($sectionHash{$layoutKey}{compressed}{algorithm} eq "xz")) + { + # Placeholder for compression partitions + } + + # Move content to final bin filename + run_command("cp $tempImages{ECC_PHASE} $final_bin_file"); + + # Clean up temp images + foreach my $image (keys %tempImages) + { + system("rm -f $tempImages{$image}"); + die "Failed deleting $tempImages{$image}" if ($?); + } + } + + # Clean up prerequisite images + foreach my $image (keys %preReqImages) + { + system("rm -f $preReqImages{$image}"); + die "Failed deleting $preReqImages{$image}" if ($?); + } + + return 0; +} + +################################################################################ +# corrupt_partition : Corrupts a single byte of a section's bin file. +# The input $protected_file is used to determine the +# unprotected offset. Some partitions have no unprotected +# section, so the file DNE. +# *Note: this should be run before ECC is injected. +################################################################################ +sub corrupt_partition +{ + my ($eyeCatch, $protected_offset, $protected_file, $bin_file) = @_; + + die "Error> Missing bin file to corrupt $bin_file" if (!-f $bin_file); + + my $section = $partitionsToCorrupt{$eyeCatch}; + my $offset = 0; + my $bin_file_size = -s $bin_file; + die "size of $bin_file undef" unless(defined $bin_file_size); + + if ($section eq $CORRUPT_PROTECTED) + { + $offset = $protected_offset; + } + elsif ($section eq $CORRUPT_UNPROTECTED) + { + # If no protected_file file exists for this partition, then that means + # there is no unprotected section. A protected_file is only created + # when there's a need to split up the partition for signing purposes. + # *Note: Must add PAGE_SIZE to protected size as it does not include + # the secure container header. + $offset = (-f $protected_file) ? (-s $protected_file)+PAGE_SIZE : 0; + die "offset undef" unless(defined $offset); + if ($offset == 0) + { + die "Error> Section $eyeCatch does not have an unprotected section to corrupt"; + } + elsif ($offset <= $protected_offset) + { + die "Error> Unprotected offset($offset) <= Protected offset($protected_offset)"; + } + } + else + { + die "Error> Unsupported --corrupt value \"$section\""; + } + + # Error checking + die "Error> corrupt offset not set" if ($offset == 0); + die "Error> Offset=$offset is past the size of the bin file to corrupt size=$bin_file_size" if ($offset >= $bin_file_size); + + # Corrupt partition + my $num_pages_to_corrupt = 1; + # If corrupting the unprotected HBI section, corrupt multiple pages in + # attempt to corrupt a page that is actually used to result in a VFS + # verify page failure. + if (($eyeCatch eq "HBI") && ($section eq $CORRUPT_UNPROTECTED)) + { + $num_pages_to_corrupt = MAX_PAGES_TO_CORRUPT; + } + for (my $i = 0; $i < $num_pages_to_corrupt; $i++) + { + my $page_offset = $i*PAGE_SIZE; + my $hex_offset = sprintf("0x%X", $offset + $page_offset); + trace(1,"Corrupting $eyeCatch $section section offset=$hex_offset"); + # dd used with seek to manipulate a bin file in-place at a specific location. + run_command("printf \'\\xaf\' | dd conv=notrunc of=$bin_file bs=1 seek=\$(($hex_offset))"); + } +} + +################################################################################ +# page_aligned_size_wo_ecc : Size of partition without ECC, rounded down to +# nearest multiple of PAGE_SIZE. +################################################################################ +sub page_aligned_size_wo_ecc +{ + my ($size) = @_; + + die "Size must be at least (9/8)*PAGE_SIZE" if ($size < ((9/8)*PAGE_SIZE)); + return POSIX::floor((($size * 8) / 9) / PAGE_SIZE) * PAGE_SIZE; +} + +################################################################################ +# truncate_sha - Truncates sha hash +# @return sha hash if already less than truncate size +# otherwise truncated sha hash +################################################################################ +sub truncate_sha +{ + my ($sha) = @_; + # Switch Perl to byte mode vs char mode. Only lasts in scope. + use bytes; + (length($sha) < SHA_TRUNCATE_SIZE)? return $sha : + return substr ($sha, 0, SHA_TRUNCATE_SIZE); +} + +################################################################################ +# genHashPageTable - Generates hash page table for PNOR partitions +# @return filename of binary hash page table content +################################################################################ +sub genHashPageTable +{ + my ($bin_file, $eyeCatch, $saltData) = @_; + + # Open the file + my $hashPageTableFile = "$bin_dir/$eyeCatch.page_hash_table"; + open (INBINFILE, "<", $bin_file) or die "Error opening file $bin_file: $!\n"; + open (OUTBINFILE, ">", $hashPageTableFile) or die "Error opening file $hashPageTableFile: $!\n"; + # set stream to binary mode + binmode INBINFILE; + binmode OUTBINFILE; + + # Enter Salt as first entry + my $salt_entry = 0; + if (defined $saltData) + { + # Use input salt data + $salt_entry = truncate_sha(sha512($saltData)); + } + else + { + # Generate random salt data + for (my $i = 0; $i < SHA_TRUNCATE_SIZE; $i++) + { + $salt_entry .= sha512(rand(0x7FFFFFFFFFFFFFFF)); + } + $salt_entry = truncate_sha(sha512($salt_entry)); + } + my @hashes = ($salt_entry); + print OUTBINFILE $salt_entry; + + # boundary + my $total_pages = POSIX::ceil((-s INBINFILE)/PAGE_SIZE); + # read buffer + my $data; + # Read data in chunks of PAGE_SIZE bytes + my $index = 1; + while ($index <= $total_pages) + { + read(INBINFILE,$data,PAGE_SIZE); + die "genHashPageTable reading of $bin_file failed" if $!; + # Add trailing zeros back in to pages at the end of the bin file. + if(length($data) < PAGE_SIZE) + { + my $pads = PAGE_SIZE - length($data); + $data .= pack ("@".$pads); + } + + # hash(salt + data) + # salt = previous entry + # data = current page + my $hash_entry = truncate_sha(sha512($hashes[$index-1].$data)); + push @hashes, $hash_entry; + $index++; + print OUTBINFILE $hash_entry; + } + + close INBINFILE or die "Error closing $bin_file: $!\n"; + close OUTBINFILE or die "Error closing $hashPageTableFile: $!\n"; + + # Pad hash page table to a multiple of page size (4K) + my $temp_file = "$bin_dir/$eyeCatch.page_hash_table.temp"; + run_command("cp $hashPageTableFile $temp_file"); + run_command("dd if=$temp_file of=$hashPageTableFile ibs=4k conv=sync"); + run_command("rm $temp_file"); + + return $hashPageTableFile; +} + +################################################################################ +# gen_test_containers : Generate test containers used in hostboot CXX tests +# Documents how the original test container files were generated for use +# by CXX tests. Note these files have been cached via "hb cacheadd". This +# simply provides transparency on how they were originally generated. +# They will not match the original file as the executable generates a +# unique container each time, even if the code is the same. +################################################################################ +sub gen_test_containers +{ + my $randPrefix = "rand-".POSIX::ceil(rand(0xFFFFFFFF)); + my %tempImages = ( + TEST_CONTAINER_DATA => "$bin_dir/$randPrefix.test.cont.bin", + PROTECTED_PAYLOAD => "$bin_dir/$randPrefix.test.protected_payload.bin" + ); + + # Create a signed test container + # name = secureboot_signed_container (no prefix in hb cacheadd) + my $test_container = "$bin_dir/secureboot_signed_container"; + run_command("dd if=/dev/zero count=1 | tr \"\\000\" \"\\377\" > $tempImages{TEST_CONTAINER_DATA}"); + run_command("$SIGNING_DIR/build -good -if $sb_hdrs{DEFAULT}{file} -of $test_container -bin $tempImages{TEST_CONTAINER_DATA} $SIGN_BUILD_PARAMS"); + + # Create a signed test container with a hash page table + # name = secureboot_hash_page_table_container (no prefix in hb cacheadd) + $test_container = "$bin_dir/secureboot_hash_page_table_container"; + run_command("dd if=/dev/urandom count=5 ibs=4096 | tr \"\\000\" \"\\377\" > $tempImages{TEST_CONTAINER_DATA}"); + $tempImages{hashPageTable} = genHashPageTable($tempImages{TEST_CONTAINER_DATA}, "secureboot_test"); + run_command("$SIGNING_DIR/build -good -if $sb_hdrs{DEFAULT}{file} -of $tempImages{PROTECTED_PAYLOAD} -bin $tempImages{hashPageTable} $SIGN_BUILD_PARAMS"); + run_command("cat $tempImages{PROTECTED_PAYLOAD} $tempImages{TEST_CONTAINER_DATA} > $test_container "); + + # Clean up temp images + foreach my $image (keys %tempImages) + { + system("rm -f $tempImages{$image}"); + die "Failed deleting $tempImages{$image}" if ($?); + } +} + +################################################################################ +# create_sb_key_transition_container +# Generate sb key transition container used for transitioning from an +# imprint to production key. +# Format: +# SB_HDR_IMPRINT_KEY[SB_HDR_PRD_KEY[4K rand blob]] +# Steps: +# 1. Generate 4K blob of random data +# 2. Sign #1 with production keys +# 3. Sign #2 with the imprint keys +################################################################################ +sub create_sb_key_transition_container +{ + my ($o_file) = @_; + + my $randPrefix = "rand-".POSIX::ceil(rand(0xFFFFFFFF)); + my %tempImages = ( + RAND_BLOB => "$bin_dir/$randPrefix.rand_blob.bin", + PRD_KEY_FILE => "$bin_dir/$randPrefix.sbkt_prod_key.bin" + ); + + # Gen 4K blob of random data + run_command("dd if=/dev/urandom of=$tempImages{RAND_BLOB} count=1 bs=4k"); + + if($openSigningTool) + { + die "Key transition not allowed in $sign_mode mode" if ($OPEN_SIGN_KEY_TRANS_REQUEST eq ""); + + # Create a signed container with new production keys + run_command("$OPEN_SIGN_KEY_TRANS_REQUEST".OP_SIGNING_FLAG + . "$sb_hdrs{SBKT}{inner}{flags} -protectedPayload $tempImages{RAND_BLOB} " + . "-out $tempImages{PRD_KEY_FILE}"); + # Sign new production key container with imprint keys + run_command("$OPEN_SIGN_REQUEST ".OP_SIGNING_FLAG + . "$sb_hdrs{SBKT}{outer}{flags} -protectedPayload $tempImages{PRD_KEY_FILE} " + . "-out $o_file"); + } + else + { + # Create a signed container with new production keys + run_command("$SIGNING_DIR/build -good -if $sb_hdrs{SBKT}{inner}{file} -of $tempImages{PRD_KEY_FILE} -bin $tempImages{RAND_BLOB} $SIGN_BUILD_PARAMS"); + # Sign new production key container with imprint keys + run_command("$SIGNING_DIR/build -good -if $sb_hdrs{SBKT}{outer}{file} -of $o_file -bin $tempImages{PRD_KEY_FILE} $SIGN_BUILD_PARAMS"); + } + + # Clean up temp images + foreach my $image (keys %tempImages) + { + system("rm -f $tempImages{$image}"); + die "Failed deleting $tempImages{$image}" if ($?); + } +} + +################################################################################ +# setCallerHwHdrFields +# Sets the caller hardware header fields in the passed in file based on +# the input hash passed in. +################################################################################ +sub setCallerHwHdrFields +{ + my ($i_callerHwHdrFields, $i_file) = @_; + + if($i_callerHwHdrFields->{configure}) + { + # If not already explicitly set, compute total container size + if(!$i_callerHwHdrFields->{totalContainerSize}) + { + $i_callerHwHdrFields->{totalContainerSize} + = -s $i_file; + die "Could not determine size of file $i_file; errno = $!" unless + defined($i_callerHwHdrFields->{totalContainerSize}); + } + my $callerHwHdr = sprintf("%016llX%016llX%016llX", + $i_callerHwHdrFields->{totalContainerSize}, + $i_callerHwHdrFields->{targetHrmor}, + $i_callerHwHdrFields->{instructionStartStackPointer}); + run_command( "echo \"$callerHwHdr\" | xxd -r -ps -seek 6 - $i_file"); + } +} + +################################################################################ +# print usage instructions +################################################################################ +sub usage +{ +print <<"ENDUSAGE"; + $programName = Manipulates bin files to prepare for building of pnor. + + Usage: + $programName --pnorlayout <layout xml file> + --systemBinFiles HBI=hostboot_extended.bin,HBEL=HBEL.bin,GUARD=EMPTY + --systemBinFiles MURANO:HBD=simics_MURANO_targeting.bin + --build-all --test --binDir <path> --secureboot --corrupt HBI + + Parms: + -h|--help Print this help text + --pnorlayout <file> PNOR Layout XML file + --build-all Indicates script should operate as if in ODE build_all + This is used to handle things that should happen once in + build_all phase and avoid parallel call issues. + --systemBinFiles [SYSTEM:]<NAME=FILE,NAME=FILE> CSV of bin files to format. Multiple '--systemBinFiles' allowed + Optional prefix 'SYSTEM:' used to specify with system bin files are being built. + For sections <NAME> that simply require zero-filling, you can pass in EMPTY or + any non-existing file. If a file DNE, the script will handle accordingly. + Example: HBI=hostboot_extended.bin,GUARD=EMPTY + MURANO:HBD=simics_MURANO_targeting.bin + --test Output test-only sections. + --secureboot Indicates a secureboot build. + --corrupt <Partition name>[= pro|unpro] (Note: requires '--secureboot') + Partition 'eyeCatch' name to corrupt a byte of. + Optional '= pro|unpro' to indicate which section of the secure container to corrupt. + Default (empty string '') is protected section. + [Note: Some sections only have a protected section so not relevant for all.] + Multiple '--corrupt' options are allowed, but note the system will checkstop on the + first bad partition so multiple may not be that useful. + Example: --corrupt HBI --corrupt HBD=unpro + --sign-mode <development|production> Indicates how to sign partitions with either development keys or production keys + --key-transition <imprint|production> Indicates a key transition is needed and creates a secureboot key transition container. + Note: "--sign-mode production" is not allowed with "--key-transition imprint" + With [--test] will transition to test dev keys, which are a fixed permutation of imprint keys. + --sb-signing-config-file Path to ini-formatted config file for production signing + + Current Limitations: + - Issues with dependency on ENGD build for certain files such as SBE. This is why [--build-all | --install-all ] are used. +ENDUSAGE +} diff --git a/src/build/buildpnor/parse-pnor b/src/build/buildpnor/parse-pnor new file mode 100755 index 000000000..720c03ec5 --- /dev/null +++ b/src/build/buildpnor/parse-pnor @@ -0,0 +1,197 @@ +#!/usr/bin/perl +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/build/buildpnor/parse-pnor $ +# +# OpenPOWER HostBoot Project +# +# Contributors Listed Below - COPYRIGHT 2015,2016 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG + +use strict; +use XML::Simple; +use Data::Dumper; +use File::Basename; +use Cwd qw(abs_path cwd); +use lib dirname abs_path($0); +use PnorUtils qw(loadPnorLayout checkFile); +use Getopt::Long qw(:config pass_through); + +################################################################################ +# Set PREFERRED_PARSER to XML::Parser. Otherwise it uses XML::SAX which contains +# bugs that result in XML parse errors that can be fixed by adjusting white- +# space (i.e. parse errors that do not make sense). +################################################################################ +$XML::Simple::PREFERRED_PARSER = 'XML::Parser'; + + +my $debug = 0; +my $help = 0; + +my %pnorLayout; +my %PhysicalOffsets; +my %globals = ( "branch" => "master" ); + +GetOptions("debug!" => \$debug, + "help" => \$help); + +my %commands = ( "display" => \&execute_display, + "help" => \&execute_help, + ); + +if ($help) +{ + execute_help(); +} + +my $command = shift @ARGV; +if ($commands{$command}) +{ + &{$commands{$command}}(); +} +else +{ + execute_help(); +} + +foreach my $arg (@ARGV) +{ + print "Unprocessed arg: $arg\n" if $debug; +} + +############################## Begin Actions ################################## + +sub execute_help +{ + my $command = shift @ARGV; + + if ($command eq "") + { + print "parse-pnor:\n"; + print " Parse a PNOR XML and get useful information\n"; + print "\n"; + print " Syntax:\n"; + print " parse-pnor [options] <tool>\n"; + print "\n"; + print " Available subtools:\n"; + foreach my $key (sort keys %commands) + { + print " $key\n"; + } + print "\n"; + print " Global options:\n"; + print " --debug Enable debug mode.\n"; + print " --help Display help on a specific tool.\n"; + } + elsif (not defined $commands{$command}) + { + die "Unknown subcommand: $command.\n"; + } + else + { + my %help = ( + "display" => +q( + Display the XML pnor layout in an easier to read format + + Use option '--gaps' to find unused space + + Options: + --layout=<xml-file> Full path to PNOR layout file [required]. + --gaps Display where gaps are [default=false]. + --verbose Display more of the PNOR layout [default=false]. +), + ); + + print "parse-pnor $command:"; + print $help{$command}; + } +} + +sub execute_display +{ + my $pnorFile = ""; + my $verbose = 0; + my $gaps = 0; + + GetOptions("layout:s" => \$pnorFile, + "verbose" => \$verbose, + "gaps" => \$gaps); + + die "Missing pnor layout" if ($pnorFile eq ""); + checkFile($pnorFile); + + my $rc = loadPnorLayout($pnorFile, \%pnorLayout, \%PhysicalOffsets); + die "Error detected from call to loadLayout()" if($rc); + + if (!$verbose) + { + print "-------------------------------------------------------- \n"; + print "Name-physicalOffset-physicalRegionSize-physicalRegionEnd \n"; + print "-------------------------------------------------------- \n"; + } + + my $curOffset = 0; + my $totalFree = 0; + # Iterate through all sections of PNOR, including TOC's + foreach my $section (sort {$a <=> $b} keys %{$pnorLayout{sections}}) + { + # Get hex format for each value + my $offset = sprintf("0x%X",$pnorLayout{sections}{$section}{physicalOffset}); + my $size = sprintf("0x%X",$pnorLayout{sections}{$section}{physicalRegionSize}); + my $end = sprintf("0x%X",hex($offset)+hex($size)); + + # Check if there is a gap between sections + if ($gaps && ($curOffset < hex($offset))) + { + print " > Gap Found: addr = ".sprintf("0x%X",$curOffset); + + # Display address and size of gap + my $gapSize = hex($offset)-$curOffset; + print " size = ".sprintf("0x%X",$gapSize)."\n"; + $totalFree += $gapSize; + $curOffset = hex($offset) + hex($size); + } + else + { + $curOffset += hex($size); + } + + # Print sections + if ($verbose) + { + print $pnorLayout{sections}{$section}{eyeCatch}."\n"; + print Dumper $pnorLayout{sections}{$section}; + print "\n"; + } + else + { + print $pnorLayout{sections}{$section}{eyeCatch}."-$offset-$size-$end\n"; + } + } + + # Display total free space + if($gaps) + { + my $hexVal = sprintf("0x%X",$totalFree); + my $kiloBytes = $totalFree/1024; + print "\n---Total Free Space = ".$totalFree." Bytes or ".$kiloBytes." KB"; + print " (".$hexVal.")\n"; + } +} diff --git a/src/build/tools/parse-pnor b/src/build/tools/parse-pnor deleted file mode 100755 index 658a658e2..000000000 --- a/src/build/tools/parse-pnor +++ /dev/null @@ -1,385 +0,0 @@ -#!/usr/bin/perl -# IBM_PROLOG_BEGIN_TAG -# This is an automatically generated prolog. -# -# $Source: src/build/tools/parse-pnor $ -# -# OpenPOWER HostBoot Project -# -# Contributors Listed Below - COPYRIGHT 2015 -# [+] International Business Machines Corp. -# -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. See the License for the specific language governing -# permissions and limitations under the License. -# -# IBM_PROLOG_END_TAG - -use strict; -use XML::Simple; -use Data::Dumper; -use Getopt::Long qw(:config pass_through); -use File::Basename; - -################################################################################ -# Set PREFERRED_PARSER to XML::Parser. Otherwise it uses XML::SAX which contains -# bugs that result in XML parse errors that can be fixed by adjusting white- -# space (i.e. parse errors that do not make sense). -################################################################################ -$XML::Simple::PREFERRED_PARSER = 'XML::Parser'; - - -my $debug = 0; -my $help = 0; - -my %pnorLayout; -my %PhysicalOffsets; -my %globals = ( "branch" => "master" ); - -GetOptions("debug!" => \$debug, - "help" => \$help); - -my %commands = ( "display" => \&execute_display, - "help" => \&execute_help, - ); - -if ($help) -{ - execute_help(); -} - -my $command = shift @ARGV; -if ($commands{$command}) -{ - &{$commands{$command}}(); -} -else -{ - execute_help(); -} - -foreach my $arg (@ARGV) -{ - print "Unprocessed arg: $arg\n" if $debug; -} - -############################## Begin Actions ################################## - -sub execute_help -{ - my $command = shift @ARGV; - - if ($command eq "") - { - print "parse-pnor:\n"; - print " Parse a PNOR XML and get useful information\n"; - print "\n"; - print " Syntax:\n"; - print " parse-pnor [options] <tool>\n"; - print "\n"; - print " Available subtools:\n"; - foreach my $key (sort keys %commands) - { - print " $key\n"; - } - print "\n"; - print " Global options:\n"; - print " --debug Enable debug mode.\n"; - print " --help Display help on a specific tool.\n"; - } - elsif (not defined $commands{$command}) - { - die "Unknown subcommand: $command.\n"; - } - else - { - my %help = ( - "display" => -q( - Display the XML pnor layout in an easier to read format - - Use option '--gaps' to find unused space - - Options: - --layout=<xml-file> Full path to PNOR layout file [required]. - --gaps Display where gaps are [default=false]. - --verbose Display more of the PNOR layout [default=false]. -), - ); - - print "parse-pnor $command:"; - print $help{$command}; - } -} - -sub execute_display -{ - my $pnorFile = ""; - my $verbose = 0; - my $gaps = 0; - - GetOptions("layout:s" => \$pnorFile, - "verbose" => \$verbose, - "gaps" => \$gaps); - - die "Missing pnor layout" if ($pnorFile eq ""); - checkFile($pnorFile); - - my $rc = loadLayout($pnorFile, \%pnorLayout, \%PhysicalOffsets); - die "Error detected from call to loadLayout()" if($rc); - - if (!$verbose) - { - print "-------------------------------------------------------- \n"; - print "Name-physicalOffset-physicalRegionSize-physicalRegionEnd \n"; - print "-------------------------------------------------------- \n"; - } - - my $curOffset = 0; - my $totalFree = 0; - # Iterate through all sections of PNOR, including TOC's - foreach my $section (sort {$a <=> $b} keys %{$pnorLayout{sections}}) - { - # Get hex format for each value - my $offset = sprintf("0x%X",$pnorLayout{sections}{$section}{physicalOffset}); - my $size = sprintf("0x%X",$pnorLayout{sections}{$section}{physicalRegionSize}); - my $end = sprintf("0x%X",hex($offset)+hex($size)); - - # Check if there is a gap between sections - if ($gaps && ($curOffset < hex($offset))) - { - print " > Gap Found: addr = ".sprintf("0x%X",$curOffset); - - # Display address and size of gap - my $gapSize = hex($offset)-$curOffset; - print " size = ".sprintf("0x%X",$gapSize)."\n"; - $totalFree += $gapSize; - $curOffset = hex($offset) + hex($size); - } - else - { - $curOffset += hex($size); - } - - # Print sections - if ($verbose) - { - print $pnorLayout{sections}{$section}{eyeCatch}."\n"; - print Dumper $pnorLayout{sections}{$section}; - print "\n"; - } - else - { - print $pnorLayout{sections}{$section}{eyeCatch}."-$offset-$size-$end\n"; - } - } - - # Display total free space - if($gaps) - { - my $hexVal = sprintf("0x%X",$totalFree); - my $kiloBytes = $totalFree/1024; - print "\n---Total Free Space = ".$totalFree." Bytes or ".$kiloBytes." KB"; - print " (".$hexVal.")\n"; - } -} - -######################### Begin Utility Subroutines ########################### - -# sub checkFile -# -# Check if file exists and is of type XML -# -# @param [in] i_layoutFile - PNOR layout file -# @return - N/A Die on failure -# -sub checkFile -{ - my $i_layoutFile = shift; - - my($filename, $dirs, $suffix) = fileparse($i_layoutFile,".xml"); - - unless(-e $i_layoutFile) - { - die "File not found: $i_layoutFile"; - } - if ($suffix ne ".xml") - { - die "File not type XML: $i_layoutFile"; - } -} - -# sub align_down -# -# Align the input to the lower end of the PNOR side -# -# @param [in] i_addr - PNOR address to align -# @param [in] i_sideSize - PNOR side size -# @return integer - aligned PNOR address -# -sub align_down -{ - my ($i_addr,$i_sideSize) = @_; - return (($i_addr) - ($i_addr)%($i_sideSize)); -} - -# sub align_up -# -# Align the input address to the higher end of the PNOR side -# -# @param [in] i_addr - PNOR address to align -# @param [in] i_sideSize - PNOR side size -# @return integer - aligned PNOR address -# -sub align_up -{ - my ($i_addr,$i_sideSize) = @_; - return ((($i_addr) + ($i_sideSize-1)) & ~($i_sideSize-1)); -} - -# sub loadLayout -# -# Load PNOR XML into hashes -# -# @param [in] i_pnorFile - PNOR XML file -# @param [in] i_pnorLayoutRef - PNOR layout hash -# @param [in] i_physicalOffsets - PNOR physical offset hash -# @return NA -# -sub loadLayout -{ - my ($i_pnorFile, $i_pnorLayoutRef, $i_physicalOffsets) = @_; - - #parse the input XML file - my $xs = new XML::Simple(keyattr=>[], forcearray => 1); - my $xml = $xs->XMLin($i_pnorFile); - - #Iterate over the <section> elements. - foreach my $sectionEl (@{$xml->{section}}) - { - my $description = $sectionEl->{description}[0]; - my $eyeCatch = $sectionEl->{eyeCatch}[0]; - my $physicalOffset = $sectionEl->{physicalOffset}[0]; - my $physicalRegionSize = $sectionEl->{physicalRegionSize}[0]; - my $side = $sectionEl->{side}[0]; - - print "description = $description, eyeCatch=$eyeCatch, physicalOffset = $physicalOffset, physicalRegionSize=$physicalRegionSize, side=$side\n" if $debug; - - $physicalOffset = hex($physicalOffset); - $physicalRegionSize = hex($physicalRegionSize); - - $$i_pnorLayoutRef{sections}{$physicalOffset}{description} = $description; - $$i_pnorLayoutRef{sections}{$physicalOffset}{eyeCatch} = $eyeCatch; - $$i_pnorLayoutRef{sections}{$physicalOffset}{physicalOffset} = $physicalOffset; - $$i_pnorLayoutRef{sections}{$physicalOffset}{physicalRegionSize} = $physicalRegionSize; - $$i_pnorLayoutRef{sections}{$physicalOffset}{side} = $side; - - #store the physical offsets of each section in a hash, so, it is easy - #to search physicalOffsets based on the name of the section (eyeCatch) - if ($side eq "sideless") - { - foreach my $metadata (@{$xml->{metadata}}) - { - foreach my $sides (@{$metadata->{side}}) - { - $$i_physicalOffsets{side}{$sides->{id}[0]}{eyeCatch}{$eyeCatch} = $physicalOffset; - } - } - } - else - { - $$i_physicalOffsets{side}{$side}{eyeCatch}{$eyeCatch} = $physicalOffset; - } - } - # Save the metadata - imageSize, blockSize, toc Information etc. - foreach my $metadataEl (@{$xml->{metadata}}) - { - # Get meta data - # Get meta data - my $imageSize = $metadataEl->{imageSize}[0]; - my $tocSize = $metadataEl->{tocSize}[0]; - my $arrangement = $metadataEl->{arrangement}[0]; - $imageSize = hex($imageSize); - $tocSize = hex($tocSize); - $$i_pnorLayoutRef{metadata}{imageSize} = $imageSize; - $$i_pnorLayoutRef{metadata}{tocSize} = $tocSize; - $$i_pnorLayoutRef{metadata}{arrangement} = $arrangement; - - my $numOfSides = scalar (@{$metadataEl->{side}}); - my $sideSize = ($imageSize)/($numOfSides); - - print "metadata: imageSize = $imageSize, arrangement = $arrangement, numOfSides: $numOfSides, sideSize: $sideSize, tocSize: $tocSize\n" if $debug; - - #determine the TOC offsets from the arrangement and side Information - #stored in the layout xml - # - #Arrangement A-B-D means that the layout had Primary TOC (A), then backup TOC (B), then Data (pnor section information). - #Similaryly, arrangement A-D-B means that primary toc is followed by the data (section information) and then - #the backup TOC. - if ($arrangement eq "A-B-D") - { - my $count = 0; - foreach my $side (@{$metadataEl->{side}}) - { - my $sideId = $side->{id}[0]; - my $primaryTOC = ($sideSize)*($count); - my $backupTOC = ($primaryTOC)+($tocSize); - - $$i_pnorLayoutRef{metadata}{sides}{$sideId}{toc}{primary} = $primaryTOC; - $$i_pnorLayoutRef{metadata}{sides}{$sideId}{toc}{backup} = $backupTOC; - - # Fill out TOC info - $$i_pnorLayoutRef{sections}{$primaryTOC}{eyeCatch} = "TOCPRIMARY"; - $$i_pnorLayoutRef{sections}{$backupTOC}{eyeCatch} = "TOCBACKUP"; - $$i_pnorLayoutRef{sections}{$primaryTOC}{physicalOffset} = $primaryTOC; - $$i_pnorLayoutRef{sections}{$backupTOC}{physicalOffset} = $backupTOC; - $$i_pnorLayoutRef{sections}{$primaryTOC}{physicalRegionSize} = $tocSize; - $$i_pnorLayoutRef{sections}{$backupTOC}{physicalRegionSize} = $tocSize; - - $count = $count + 1; - print "A-B-D: side:$sideId primaryTOC:$primaryTOC, backupTOC:$backupTOC\n" if $debug; - } - } - elsif ($arrangement eq "A-D-B") - { - foreach my $side (@{$metadataEl->{side}}) - { - my $sideId = $side->{id}[0]; - my $hbbAddr = $$i_physicalOffsets{side}{$sideId}{eyeCatch}{"HBB"}; - my $primaryTOC = align_down($hbbAddr, $sideSize); - my $backupTOC = align_up($hbbAddr, $sideSize) - $tocSize; - - $$i_pnorLayoutRef{metadata}{sides}{$sideId}{toc}{primary} = $primaryTOC; - $$i_pnorLayoutRef{metadata}{sides}{$sideId}{toc}{backup} = $backupTOC; - - # Fill out TOC info - $$i_pnorLayoutRef{sections}{$primaryTOC}{eyeCatch} = "TOCPRIMARY"; - $$i_pnorLayoutRef{sections}{$backupTOC}{eyeCatch} = "TOCBACKUP"; - $$i_pnorLayoutRef{sections}{$primaryTOC}{physicalOffset} = $primaryTOC; - $$i_pnorLayoutRef{sections}{$backupTOC}{physicalOffset} = $backupTOC; - $$i_pnorLayoutRef{sections}{$primaryTOC}{physicalRegionSize} = $tocSize; - $$i_pnorLayoutRef{sections}{$backupTOC}{physicalRegionSize} = $tocSize; - - print "A-D-B: side:$sideId HBB:$hbbAddr, primaryTOC:$primaryTOC, backupTOC:$backupTOC\n" if $debug; - } - } - else - { - print "Arrangement:$arrangement is not supported" if $debug; - exit(1); - } - } - - print Dumper %pnorLayout if $debug; - print Dumper %PhysicalOffsets if $debug; - return 0; -}
\ No newline at end of file |