diff options
author | Nick Bofferding <bofferdn@us.ibm.com> | 2019-02-07 16:11:15 -0600 |
---|---|---|
committer | Daniel M. Crowell <dcrowell@us.ibm.com> | 2019-02-15 19:34:21 -0600 |
commit | d77319a7e204e83156e72fa482fc057a90e80868 (patch) | |
tree | 9d7345a3f0c18dc67045a2e2b33ec9509930a829 /src/build/buildpnor | |
parent | e2b54b9aeeb83db822c4ea5f6b16e043e7ae0ace (diff) | |
download | talos-hostboot-d77319a7e204e83156e72fa482fc057a90e80868.tar.gz talos-hostboot-d77319a7e204e83156e72fa482fc057a90e80868.zip |
Support tool that can package UCD* flash images into a binary
- Implements new tool that takes UCD* flash images, aggregates them together,
and puts a TOC on the front. Supported UCD models include UCD9090 and UCD90120A
- Distributes the tool to FSP and OpenPOWER trees
Change-Id: I21af0d0dc5b9234d9be50227c2752a136bb2aad6
RTC: 201992
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/71548
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com>
Reviewed-by: Ilya Smirnov <ismirno@us.ibm.com>
Reviewed-by: Michael Baiocchi <mbaiocch@us.ibm.com>
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Diffstat (limited to 'src/build/buildpnor')
-rwxr-xr-x | src/build/buildpnor/buildUcdFlashImages.pl | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/src/build/buildpnor/buildUcdFlashImages.pl b/src/build/buildpnor/buildUcdFlashImages.pl new file mode 100755 index 000000000..33b6655f1 --- /dev/null +++ b/src/build/buildpnor/buildUcdFlashImages.pl @@ -0,0 +1,485 @@ +#!/usr/bin/perl +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/build/buildpnor/buildUcdFlashImages.pl $ +# +# OpenPOWER HostBoot Project +# +# Contributors Listed Below - COPYRIGHT 2013,2019 +# [+] 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 File::Basename; +use Getopt::Long; +use Pod::Usage; + +# These must be kept in sync with Hostboot source code +use constant IMG_TYPE_DATA_FLASH => 0; +use constant MAX_DEVICE_ID_ASCII => 31; +use constant NULL_SIZE => 1; +use constant EYECATCH_SIZE => 8; +use constant EYECATCH => "UCDFLSH"; + +# These can be arbitrarily changed if needed +use constant TOC_ALIGNMENT => 0x80; +use constant TOC_ENTRY_SIZE => 0x40; +use constant FLASH_IMAGES_OFFSET => 0x1000; +use constant FLASH_IMAGE_ALIGNMENT => 0x1000; +use constant TOC_MAJOR => 0x00000001; +use constant TOC_MINOR => 0x00000000; + +my $TRAC_ERR = 0; +# 0=errors, >0 for more traces, leaving at 1 to keep key traces. +my $g_trace = 1; + +my $progName = File::Basename::basename $0; +my $outputFile="ucd-flash.bin"; +my $outputDir="."; +my @files; +my $cfgHelp=0; +my $cfgMan=0; +my $verbose=0; + +GetOptions("output-file=s" => \$outputFile, + "output-dir=s" => \$outputDir, + "image=s" => \@files, + "help" => \$cfgHelp, + "verbose" => \$verbose, + "man" => \$cfgMan ) || pod2usage(-verbose => 0); + +pod2usage(-verbose => 1) if $cfgHelp; +pod2usage(-verbose => 2) if $cfgMan; + +if (keys %{{ map {$_, 1} @files }} != @files) +{ + die "One or more non-unique --image arguments supplied"; +} + +if($verbose) +{ + print "Output file = $outputFile\n"; + print "Output dir = $outputDir\n"; + print "Input images:\n"; + for my $file (@files) + { + print " $file\n"; + } +} + +my %flashImages; +for my $file (@files) +{ + processInputFile($file,$outputDir,\%flashImages); +} + +my $rc = genOutputImage($outputDir,$outputFile,\%flashImages); +if($rc != 0) +{ + trace(0, "$progName: Error detected from call to genOutputImage(). " + . " Exiting"); + exit $rc; +} + +################################################################################ +# @brief Upward aligns the input value to the requested boundary +# +# @param[in] i_value The value to align +# @param[in] i_boundary The boundary to align to +# +# @return The input value, aligned to the requested boundary +################################################################################ + +sub alignto +{ + my $i_value = shift; + my $i_boundary = shift; + + return ((($i_value) + (($i_boundary)-1)) & ~(($i_boundary)-1)); +} + +################################################################################ +# @brief Processes an input file to pull information from its name and content +# to be used when preparing the output file's TOC. Also strips out +# unneeded content (like comments) and writes the remainder to an +# intermediate file with .stripped extension, which will be fed into +# the data portion of the final output file. +# +# @param[in] i_fileName Name of file +# @param[in] i_outputDir Dir to emit intermediate file to +# @param[in] i_flashImages Hash where TOC information will be recorded +# +# @return N/A +################################################################################ + +sub processInputFile +{ + my ($i_fileName, $i_outputDir, $i_flashImages) = @_; + my $this_func = (caller(0))[3]; + + my ($name,$path,$suffix) = fileparse($i_fileName); + + # Enforce the input file naming convention + if($name !~ + /\AUCD[[:alnum:]]+\-data\-pu[0-3]_e[0-3]_p(0?[0-9]|1[0-2])_a[[:xdigit:]]{2}\.csv\Z/) + { + die "Unexpected file name ($i_fileName); must be in the format: " + . "UCDV-data-puW_eX_pY_aZZ.csv, where V is a UCD device ID, " + . "W=proc position (0-3), " + . "X=engine # (0-3), Y=port (0-9 or 00-09 or 10-12), " + . "ZZ=hex address (00->FF)"; + } + + # Break the file name into the device ID. update type, i2c info, and the csv + # extension. The extension will be discarded. + my ($base,$csv) = split(/\./,$name); + my ($deviceId,$imageType,$i2cInfo) = split (/-/,$base ); + + if(length $deviceId > MAX_DEVICE_ID_ASCII ) + { + die "Device ID of $deviceId was > " . MAX_DEVICE_ID_ASCII + . " characters"; + } + + if( $deviceId ne "UCD9090" + and $deviceId ne "UCD90120A") + { + die "Unsupported UCD device $deviceId"; + } + + if($imageType ne "data") + { + die "Only data flash images supported (not $imageType type)"; + } + + # Extract the MFR_REVISION out of the file + my $mfrRevision=undef; + open my $fh, '<:encoding(UTF-8)', $i_fileName + or die "Failed to open $i_fileName"; + my $optimizedFile = $i_outputDir . "/" . $name . ".stripped"; + open my $ofh, '>:encoding(UTF-8)', $optimizedFile + or die "Failed to open $optimizedFile"; + + while (my $line = <$fh>) + { + # Match below string (but allow any trailing number): + # Comment,Write MFR_REVISION 02 + if($line =~ /\AComment,Write MFR_REVISION [[:xdigit:]]{2}\s*\Z/) + { + if(!defined $mfrRevision) + { + $mfrRevision = $line; + $mfrRevision =~ s/\AComment,Write MFR_REVISION //; + } + else + { + die "Duplicate MFR_REVISION lines detected in $i_fileName"; + } + } + + # Copy the input to the output, except for comment lines + if($line !~ /\AComment/) + { + print $ofh $line; + } + } + close $fh or die "Failed to close input file: $i_fileName"; + close $ofh or die "Failed to close optimized ouput file: $optimizedFile"; + + if(!defined $mfrRevision) + { + die "Could not determine MFR_REVISION from $i_fileName"; + } + + # Strip the prefixing identifiers off the i2c info + my ($i2cProc,$i2cEngine,$i2cPort,$i2cAddress) = split /_/,$i2cInfo; + $i2cProc =~s/\Apu//; + $i2cEngine =~s/\Ae//; + $i2cPort =~s/\Ap//; + $i2cAddress =~s/\Aa//; + + $$i_flashImages{$i_fileName}{deviceId} = $deviceId; + $$i_flashImages{$i_fileName}{imageType} = IMG_TYPE_DATA_FLASH; + $$i_flashImages{$i_fileName}{i2cProc} = $i2cProc; + $$i_flashImages{$i_fileName}{i2cEngine} = $i2cEngine; + $$i_flashImages{$i_fileName}{i2cPort} = $i2cPort; + $$i_flashImages{$i_fileName}{i2cAddress} = $i2cAddress; + $$i_flashImages{$i_fileName}{mfrRevision} = $mfrRevision; + $$i_flashImages{$i_fileName}{optimizedFile} = $optimizedFile; + + trace(10, "$this_func: File: $i_fileName, Device ID: $deviceId, " + . "Image type: " . IMG_TYPE_DATA_FLASH + . " Master proc ID: $i2cProc, I2C engine: $i2cEngine, " + . "I2C port: $i2cPort, I2C address: $i2cAddress, " + . "MFR_REVISION: $mfrRevision, Optimized file: $optimizedFile"); + + # No return code +} + +################################################################################ +# @brief Builds the final UCD flash image file +# +# @param[in] i_outputDir Directory to generate the output in +# @param[in] i_outputFile Name of file to generate the output in +# @param[in] i_flashImages Reference to hash containing flash image information +# +# @return Return code indicating success or failure +# @retval 0 Successfully generated intended output image +# @retval !0 Failed to generate intended output image +################################################################################ + +sub genOutputImage +{ + my ($i_outputDir,$i_outputFile,$i_flashImages) = @_; + my $this_func = (caller(0))[3]; + trace(4, "$this_func: >>Enter"); + + my $rc = 0; + my $curOffset = FLASH_IMAGES_OFFSET; + + # Open output file + my $outputPath = $i_outputDir . "/" . $i_outputFile; + my $OFH; + open( $OFH, ">:raw", $outputPath) + or die "Can't open $outputPath for writing"; + + # Build the header and table of contents (TOC) + + # TOC header format: + # + # char[8] Eyecatcher "UCDFLSH" + NULL + # uint32_t TOC major version; Increment if change will break existing code + # uint32_t TOC minor version; Increment if change won't break existing code + # uint32_t Number of flash image TOC entries + # uint32_t Size of each TOC entry + # uint32_t Offset to 0th TOC entry from start of file + # char[X] Pad to start of N TOC entries + # + # TOC entry format: + # + # char[32] Device ID "UCD9090" or "UCD90120A" + NULL. Max 31 non-null + # chars left justified. Others could be supported in future. + # uint8_t Flash image type (0=data flash image, others reserved) + # uint8_t Processor position (pu value of ECMD string) + # uint8_t I2C engine + # uint8_t I2C port + # uint8_t I2C address + # uint8_t Pad byte + # uint16_t MFR_REVISION (2 bytes ASCII, i.e. "02" = 0x30 0x32) + # uint32_t Offset to flash image from start of file + # uint32_t Flash image size + # char[16] Pad to TOC entry size (align next TOC entry @ 16 byte boundary) + # + # Flash images start at 4k and are 4k aligned + + my %flashImageOffsets; + + my $header = pack("Z" . EYECATCH_SIZE, EYECATCH); + + # uint32_t: major, uint32_t: minor + $header .= pack('N', TOC_MAJOR); + $header .= pack('N', TOC_MINOR); + + # Write # of flash images + my $flashImages = keys %{$i_flashImages}; + $header .= pack('N', $flashImages); + + # Write size of TOC entry + $header .= pack('N', TOC_ENTRY_SIZE); + + # Write offset to 0th TOC entry + my $usedHeaderLength = length($header) + 4; + my $offsetToToc = alignto($usedHeaderLength,TOC_ALIGNMENT); + $header .= pack('N', $offsetToToc); + + # Write pads in front of TOC entries + my $endOfHeaderPads = $offsetToToc - length $header; + $header .= pack("C$endOfHeaderPads"); + + my $contentSansImages = $header; + + #Insert TOC entry for each flash image + for my $key (keys %{$i_flashImages}) + { + trace(2, "$this_func: Inserting header for $key"); + + # Write device ID + my $maxSize = MAX_DEVICE_ID_ASCII + NULL_SIZE; + my $tocEntry .= pack("Z" . $maxSize, $$i_flashImages{$key}{deviceId}); + + # Write flash image type + $tocEntry .= pack('C', $$i_flashImages{$key}{imageType}); + + # Write I2C information + $tocEntry .= pack('C', $$i_flashImages{$key}{i2cProc}); + $tocEntry .= pack('C', $$i_flashImages{$key}{i2cEngine}); + $tocEntry .= pack('C', $$i_flashImages{$key}{i2cPort}); + $tocEntry .= pack('H*', $$i_flashImages{$key}{i2cAddress}); + + # Write single pad byte + $tocEntry .= pack('C1'); + + # Write MFR_REVISION + $tocEntry .= pack('A2', $$i_flashImages{$key}{mfrRevision}); + + # Write offset to flash image (from start of file) + $tocEntry .= pack('N', $curOffset); + + # Write size of flash image + my $fileSize = -s $$i_flashImages{$key}{optimizedFile}; + if(!defined $fileSize) + { + die "Failed reading file size for " + . "$$i_flashImages{$key}{optimizedFile} "; + } + $tocEntry .= pack('N', $fileSize); + + # Pad remainder of TOC entry to the alignment boundary + my $endOfTocEntryPads = alignto(length($tocEntry), + TOC_ENTRY_SIZE) - length($tocEntry); + $tocEntry .= pack("C$endOfTocEntryPads"); + + # Verify we didn't exceed the TOC entry size + my $len = length $tocEntry; + if($len > TOC_ENTRY_SIZE) + { + die "TOC entry size ($len) exceeds max allowable size of " + . TOC_ENTRY_SIZE; + } + + # Save the flash image insertion offset + $flashImageOffsets{$key} = $curOffset; + + # Determine next nearest flash image alignment boundary + $curOffset += $fileSize; + $curOffset = alignto($curOffset,FLASH_IMAGE_ALIGNMENT); + + $contentSansImages .= $tocEntry; + } + + # Make sure we didn't bleed into flash content area + my $contentSansImagesSize = length $contentSansImages; + if($contentSansImagesSize > FLASH_IMAGES_OFFSET) + { + die "Header and TOC (length=$contentSansImagesSize) " + . "overruns flash image content starting at " + . FLASH_IMAGES_OFFSET; + } + + # Write the header and TOC + print $OFH $contentSansImages + or die "Failed to serialize header and TOC to $outputPath"; + + close $OFH or die "Failed to close $outputPath"; + + #Insert actual image for each flash image provided + for my $key (keys %{$i_flashImages}) + { + trace(2, "$this_func: Inserting flash image $key, " + . "offset=$flashImageOffsets{$key}"); + + my $seekOffset = $flashImageOffsets{$key}; + my $inFile = $$i_flashImages{$key}{optimizedFile}; + + my $ddCmd = "dd if=$inFile of=$outputPath bs=1 seek=$seekOffset"; + system ( $ddCmd ) == 0 or die "Couldn't Write $inFile to $outputPath!"; + } + + trace(4, "$this_func: <<Exit"); + + return $rc; +} + +################################################################################ +# traceErr +################################################################################ + +sub traceErr +{ + my $i_string = shift; + trace($TRAC_ERR, $i_string); +} + +################################################################################ +# trace +################################################################################ + +sub trace +{ + my ($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"; + } +} + +__END__ + +=head1 NAME + +buildUcdFlashImage.pl + +=head1 SYNOPSIS + +buildUcdFlashImage.pl [..] + +=head1 OPTIONS + +=over 8 + +=item B<--help> + +Prints a brief help message and exits. + +=item B<--man> + +Prints the manual page and exits. + +=item B<--output-file> + +File containing an aggregation of UCD device flash images prefixed by a table +of contents. + +=item B<--output-dir> + +Path to directory to emit the output files to. + +=item B<--image> + +File containing a UCD flash update script. More than one --image +argument can be provided. If no images are supplied, the output file will +consist of a TOC indicating no valid flash images. + +=back + +=head1 DESCRIPTION + +B<buildUcdFlashImage.pl> will process a set of one or more UCD flash update +scripts in .csv format, strip out the comments, aggregate them together, and +prefix the aggregate with a table of contents. UCD devices allow flash updates +both data and program flash, however currently firmware only supports updating +UCD data flash. + +=cut + |