summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwhs <whs@us.ibm.com>2016-05-25 09:26:29 -0500
committerDaniel M. Crowell <dcrowell@us.ibm.com>2019-07-03 08:35:06 -0500
commit916c31cdf8028b4a5f9ddf95b766a4b83353c175 (patch)
treed1b5949214a75dac0cdc9dd0a8e316bfaff93ab3
parentd2d561f722aa8f85f60924718263a4b17fa840ba (diff)
downloadtalos-hostboot-916c31cdf8028b4a5f9ddf95b766a4b83353c175.tar.gz
talos-hostboot-916c31cdf8028b4a5f9ddf95b766a4b83353c175.zip
Create tool to generate memory vpd keyword mappings
Process input directory of vpd text files and create binary keyword files and a mapping binary keyword file. Change-Id: I8c1a5e03154b4d516ac20d10b222a84e17b244fe RTC: 154580 Reviewed-on: http://ralgit01.raleigh.ibm.com/gerrit1/25009 Tested-by: Jenkins Server Reviewed-by: Brian R. Silver <bsilver@us.ibm.com> Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com> Reviewed-by: Jennifer A. Stofer <stofer@us.ibm.com> Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/79897 Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com> Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com> Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com> Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com>
-rwxr-xr-xsrc/import/tools/genMemVpd.pl1291
1 files changed, 1291 insertions, 0 deletions
diff --git a/src/import/tools/genMemVpd.pl b/src/import/tools/genMemVpd.pl
new file mode 100755
index 000000000..172f72e8a
--- /dev/null
+++ b/src/import/tools/genMemVpd.pl
@@ -0,0 +1,1291 @@
+#!/usr/bin/perl
+# IBM_PROLOG_BEGIN_TAG
+# This is an automatically generated prolog.
+#
+# $Source: src/import/tools/genMemVpd.pl $
+#
+# OpenPOWER HostBoot Project
+#
+# Contributors Listed Below - COPYRIGHT 2016,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
+
+################################################################################
+# This script creates binary vpd files to be used to build the direct memory
+# eeprom image. There are two 'types', MR and MT. MR is for the 'memory rotator'
+# direct memory attributes and MT is for the 'memory terminator' direct memory
+# attributes.
+#
+# For MR and MT, there can be unique attribute values based on the
+# MCS position, the data rate of the system, and the rank count of the dimms
+# plugged in.
+#
+# There are 16 possible MCS positions (4 possible procs x 4 MCS
+# positions). MRW attribute ATTR_MEMVPD_POS, similiar to ATTR_FAPI_POS,
+# but will differ on multiple node systems, goes from 0 to 15. For a
+# 2 proc system, there will only be 8 MCSs, but up to 16 is supported by the
+# script for the general case.
+#
+# It was 'decided' to support up to 4 data rates per system. The four values
+# are specified in MRW ATTR_MEMVPD_FREQS_MHZ. The four values are passed to
+# this script since the script has no connection to the MRW.
+#
+# Each dimm can have rank count 0 (not there), 1, 2, or 4 ranks. Each
+# 'pair' combination (dimm0 rank count by dimm1 rank count), may need
+# unique vpd attribute values. There are 16 combinations of dimm0 rank count by
+# dimm1 rank count. For MR, the rank count is summarized into drop 1 for
+# those combinations with only one dimm (a 0 in the pair) and drop 2 for
+# those combinations with two dimms (does not have a 0 in the pair).
+#
+# There are 1024 (16 MCS positions x 4 freqencies x 16 dimm rank pairs)
+# configurations. In a particular system, some may not be supported and
+# many commbinations will use the same attribute values. It was 'decided'
+# to support up to 36 unique sets of values. For MR, they are stored in
+# vpd keywords J0..J9,JA..JZ and for MT in vpd keywords X0..X9,XA..XZ.
+# In practice, the eeprom will likely not be big enough for this many
+# keywords (each is 255 bytes), so the actual number will be less.
+#
+# The 'values' for the attributes in a vpd keyword are provided in files
+# with a '.vpd' extension and is in the format of an attribute override file,
+# but only has direct values (no enumerations) so that this script does not
+# have to be built to a particular build, like the attribute override tool.
+#
+# The vpd text file (.vpd extension) also has a header to specify the data
+# rate frequencies and rank count pairs supported by the file. The 'target'
+# line specifies the MCS positions supported. There can be multiple target
+# lines, similiar to an attribute override file, but only one header which
+# must preceed the first target line.
+#
+# example partial file: template_mr_00_freq_2400_2133_drop_2_1.vpd
+#
+# #DATA_RATE = 2400, 2133
+# #NUM_DROPS = 2, 1
+# target = k0:n0:s0:p9.mcs:p0,1:call
+# #MSS:description:MR Keyword Layout Version Number. Increases when attributes
+# # are added, removed, or redefined. Does not reset.
+# #MSS:mssUnit:num
+# ATTR_MSS_VPD_MR_0_VERSION_LAYOUT u8 0x00
+# #MSS:description:MR Keyword Data Version Number. Increases when data changes
+# # with the above layout version. Resets when layout version number
+# # increments.
+# #MSS:mssUnit:num
+# ATTR_MSS_VPD_MR_1_VERSION_DATA u8 0x00
+# #MSS:description:Hash Signature for the MT Keyword. The hash signature is
+# # 32bits for 256 bytes of data.
+# #MSS:mssUnit:hash
+# ATTR_MSS_VPD_MR_2_SIGNATURE_HASH u32 0x00000000
+# #MSS:description:Phase rotator delay value of command/address of A## in
+# # ticks. Ticks are 1/128 of one cycle of clock.
+# #MSS:mssUnit:tick
+# ATTR_MSS_VPD_MR_MC_PHASE_ROT_ADDR_A00[0] u8[2] 0x03
+# ATTR_MSS_VPD_MR_MC_PHASE_ROT_ADDR_A00[1] u8[2] 0x08
+#
+# example partial file: template_mt_00_dimm0dimm1_10_20_11_22.vpd
+#
+# #RANK_CONFIG = 0x10, 0x20, 0x11, 0x22
+# target = k0:n0:s0:p9.mcs:p0,1:call
+# #MSS:description:MT Keyword Layout Version Number. Increases when attributes
+# # are added, removed, or redefined. Does not reset.
+# #MSS:mssUnit:num
+# ATTR_MSS_VPD_MT_0_VERSION_LAYOUT u8 0x00
+# #MSS:description:MT Keyword Data Version Number. Increases when data changes
+# # with the above layout version. Resets when layout version number increments
+# #MSS:mssUnit:num
+# ATTR_MSS_VPD_MT_1_VERSION_DATA u8 0x00
+# #MSS:description:Hash Signature for the MT Keyword. The hash signature is
+# # 32bits for 256 bytes of data.
+# #MSS:mssUnit:encode
+# ATTR_MSS_VPD_MT_2_SIGNATURE_HASH u32 0x00000000
+# #MSS:description:Memory Controller side Read Vref setting. Dividing by 1000
+# # gives you percentage of Vdd
+# #MSS:enum:VDD40375 = 40375, ...
+# #MSS:mssUnit:percent of Vdd
+# ATTR_MSS_VPD_MT_VREF_MC_RD[0] u32[2] 0x000131AA
+# ATTR_MSS_VPD_MT_VREF_MC_RD[1] u32[2] 0x000131AA
+#
+# The script parses the ATTR_ lines for values to build the binary file,
+# but does not care about the specifics, other then data type (u8, u16,..)
+# and value.
+#
+# The script also builds a "mapping" from the configurations in the vpd
+# text files to the generated keyword files. The mapping is provided in
+# a binary MR and MT keyword file. At run time, the getVPD call is passed
+# a VPDInfo structure with frequency and dimm counts. The target's MEMVPD_POS,
+# frequency, and dimm rank count pair is used with the MR or MT keyword
+# mapping to select a vpd keyword (X0..XZ or J0..JZ), returning the values.
+#
+# More details...
+#
+# The order of the frequencies passed to the script and the order in the MRW
+# is significant as the "index" into the list is encoded into the mapping
+# which will be matched to the "index" of the VPDInfo frequency to the MRW
+# list. The frequency list is a required parameter.
+#
+# The format of the file name of the vpd text files is:
+# $SystemName_$KeyWordType_$DecoderVersion_$KeyWordDependentInfo.vpd
+# $KeyWordType is expected to be MR or MT (case insensitive)
+# The script picks the files with the highest $DecoderVersion and
+# replaces the output files.
+# $SystemName_$KeyWordType is a required parameter, called the 'prefix'.
+#
+# The input and output directories can be optionally specified as parameters.
+# The same directory can be used for both. The default is to use the
+# directory where the script is (./).
+#
+# The script also creates 3 information files.
+# $SystemName_$KeyWordType_mapping.csv has a text version of the mapping
+# keyword for human readable convenience.
+# $SystemName_$KeyWordType_trace.txt has a detailed execution trace for
+# debuging.
+# $SystemName_$KeyWordType_report.csv shows the mapped to keyword
+# for each of the 1024 possible configurations along with the vpd text
+# file it came from. There should not be any "duplicates", meaning there
+# was a overlap in the vpd text file specifications. Either the vpd text
+# files need to be fixed, or there is a bug in the script. The report
+# can also be used to debug if at run time the wrong vpd is thought to have
+# been returned.
+
+#TODO RTC:154580 support split eeproms
+
+use strict;
+
+################################################################################
+# Use of the following packages
+################################################################################
+
+use Getopt::Long;
+
+################################################################################
+# define constants and data type conventions
+################################################################################
+
+# a number is a hash with these members
+use constant NUM_VALUE => "num_value"; #numeric value
+use constant NUM_SIZE => "num_size"; #number of bytes
+
+# a fileInfo is a hash with these members
+use constant FILE_NAME => "file_name"; #name of the binary file to write
+use constant FILE_SIZE => "file_size"; #final size to create
+use constant FILE_DATA => "file_data"; #array to hold data until written
+use constant FILE_PTR => "file_ptr" ; #current end of data pointer
+
+# a configuration is a hash with these members
+use constant CONF_MCS => "config_mcs_mask";
+use constant CONF_FREQ => "config_freq_mask";
+use constant CONF_RANK => "config_rank_mask";
+use constant CONF_KEY_CHAR => "config_keyword_character";
+use constant CONF_VPD_TEXT_FILE => "config_vpd_text_file";
+use constant CONF_BIN_FILE => "config_bin_file";
+
+# global constants
+use constant VPD_BIN_FILE_SIZE => 255 ; #all vpd binary key word file size
+
+################################################################################
+# configuration structures.
+# These masks (and the version) are written to the binary mapping vpd keyword
+# and used by the decode hwp.
+################################################################################
+use constant MAPPING_LAYOUT_VERSION => 1; #version of decode algorithm
+
+my %g_freqMask = ( #MRW frequency index to mask
+ 0 => 0x80, #first listed MRW freq...
+ 1 => 0x40,
+ 2 => 0x20,
+ 3 => 0x10); #last listed MRW freq
+use constant FREQ_ALL => 0xf0;
+
+my %g_rankMask = ( #dimm rank count pair to mask
+ 0x00 => 0x8000,
+ 0x01 => 0x4000, #dimm0 rank count=0 dimm1 rank count=1
+ 0x02 => 0x2000,
+ 0x04 => 0x1000,
+ 0x10 => 0x0800,
+ 0x11 => 0x0400,
+ 0x12 => 0x0200, #dimm0 rank count=1 dimm1 rank count=2
+ 0x14 => 0x0100,
+ 0x20 => 0x0080,
+ 0x21 => 0x0040,
+ 0x22 => 0x0020,
+ 0x24 => 0x0010,
+ 0x40 => 0x0008,
+ 0x41 => 0x0004,
+ 0x42 => 0x0002, #dimm0 rank count=4 dimm1 rank count=2
+ 0x44 => 0x0001);
+use constant RANK_DROP1 => 0x7888; #pairs with just one dimm
+use constant RANK_DROP2 => 0x0777; #pairs with two dimms
+
+my %g_mcsMask = ( #MCS MEMVPD_POS to mask
+ 0 => 0x8000, #proc=0 mcs position=0 memvpd_pos=0
+ 1 => 0x4000,
+ 2 => 0x2000,
+ 3 => 0x1000,
+ 4 => 0x0800, #proc=1 mcs position=0 memvpd_pos=4
+ 5 => 0x0400,
+ 6 => 0x0200,
+ 7 => 0x0100,
+ 8 => 0x0080, #proc=2 mcs position=0 memvpd_pos=8
+ 9 => 0x0040,
+ 10 => 0x0020,
+ 11 => 0x0010,
+ 12 => 0x0008, #proc=3 mcs position=0 memvpd_pos=12
+ 13 => 0x0004,
+ 14 => 0x0002,
+ 15 => 0x0001);
+use constant MCS_ALL => 0xffff;
+use constant MAX_NUM_PROCS => 4;
+use constant MAX_POSITION => 3;
+
+################################################################################
+# Global data
+################################################################################
+my @g_MrwFreq = (); #MRW MEMVPD_FREQS_MHZ input parameter
+my %g_configs = (); #hash of configuration hashes (a list of all configs)
+my $g_tarType = ""; #supported target types (MR or MT)
+
+################################################################################
+# Main flow:
+# - get parameters and validate
+# - get list of vpd text files to process
+# - process each vpd text file
+# - construct mapping and keyword binary file
+# - construct report
+################################################################################
+
+sub main{ }
+my $cfgMrwMemVpdFreqsMhz = undef;
+my $cfgPrefix = undef;
+my $cfgInputVpdTextDir = ".";
+my $cfgOutputVpdBinDir = ".";
+my $cfgHelp = 0;
+my $cfgVerbose = 0;
+
+# Process command line parameters, issue help text if needed
+GetOptions("mrw-MEMVPD-FREQS-MHZ:s" => \$cfgMrwMemVpdFreqsMhz,
+ "prefix:s" => \$cfgPrefix,
+ "input-vpd-text-dir:s" => \$cfgInputVpdTextDir,
+ "output-vpd-bin-dir:s" => \$cfgOutputVpdBinDir,
+ "help" => \$cfgHelp,
+ "verbose" => \$cfgVerbose );
+
+if ($cfgHelp)
+{
+ display_help();
+ exit(0);
+}
+
+# Check mandatory parameters
+if ($cfgMrwMemVpdFreqsMhz eq undef)
+{
+ print STDERR "\n==>mrw-MEMVPD-FREQS-MHZ is a required parameter\n";
+}
+if ($cfgPrefix eq undef)
+{
+ print STDERR "\n==>prefix is a required parameter\n";
+}
+
+if ($cfgMrwMemVpdFreqsMhz eq undef ||
+ $cfgPrefix eq undef)
+{
+ display_help();
+ exit(1);
+}
+
+# Validate vpd type
+{
+ (my $system,$g_tarType) = split(/\_/,$cfgPrefix);
+ $g_tarType = uc $g_tarType;
+ if ( ("MR" ne $g_tarType) &&
+ ("MT" ne $g_tarType))
+ {
+ fatal("error in --prefix parameter: $cfgPrefix".
+ " unsupported target type = $g_tarType");
+ }
+}
+
+# Parse parameter MRW frequencys
+{
+ my ($freq0,$freq1,$freq2,$freq3) = split(',',$cfgMrwMemVpdFreqsMhz);
+ @g_MrwFreq = (str2value($freq0),
+ str2value($freq1),
+ str2value($freq2),
+ str2value($freq3));
+}
+
+# Ensure directories consistently end with a /
+{
+ local $/ = '/'; #use temporary version of $/ for the chomp
+ chomp($cfgInputVpdTextDir);
+ $cfgInputVpdTextDir .= "/";
+
+ chomp($cfgOutputVpdBinDir);
+ $cfgOutputVpdBinDir .= "/";
+}
+
+# Show parameters
+{
+ verbose("Parameters=");
+ verbose(" MRW MEMVPD_FREQS_MHZ ="
+ ." $g_MrwFreq[0],$g_MrwFreq[1],$g_MrwFreq[2],$g_MrwFreq[3]");
+ verbose(" Prefix = $cfgPrefix");
+ verbose(" Input vpd text file directory = $cfgInputVpdTextDir");
+ verbose(" Ouput vpd bin file directory = $cfgOutputVpdBinDir");
+}
+
+# Get the list of input vpd text files from the input directory
+my @vpdTextFiles = getVpdTextFileList($cfgInputVpdTextDir,$cfgPrefix);
+{
+ my $numVpdTextFiles = scalar(@vpdTextFiles);
+ if ($numVpdTextFiles == 0)
+ {
+ fatal("No input vpd text files");
+ }
+ verbose("Input Vpd Text Files ($numVpdTextFiles)=");
+ foreach my $file(@vpdTextFiles)
+ {
+ verbose(" $file");
+ }
+}
+
+# Process vpd text files
+foreach my $file(@vpdTextFiles)
+{
+ processVpdTextFile($file);
+}
+
+# Create VPD mapping binary file
+createMappingFile();
+
+# Create report
+createReport();
+
+# Show what files have been created
+{
+ my @keys = sort {$a <=> $b} keys %g_configs;
+ my $numBinVpdFiles = (scalar @keys) + 1;
+ verbose("Output binary Vpd Files ($numBinVpdFiles)=");
+ verbose(" $cfgPrefix"."_".$g_tarType.".bin");
+ foreach my $key (@keys)
+ {
+ my $ref_config = $g_configs{$key};
+ verbose(" $ref_config->{CONF_BIN_FILE}");
+ }
+ verbose("Support files created(3)=");
+ verbose(" $cfgPrefix"."_map.csv");
+ verbose(" $cfgPrefix"."_report.csv");
+ verbose(" $cfgPrefix"."_trace.csv");
+}
+
+verbose("Sucessful completion");
+exit(0);
+
+################################################################################
+# Get the list of input vpd text files from input directory
+#
+# file name format =
+# $SystemName_$KeyWordType_$DecoderVersion_$KeyWordDependentInfo.vpd
+# Return the files of the highest $DecoderVersion. This is the mem
+# team decode version of the vpd data, not the decode of the selection
+# criteria done by p9_get_mem_vpd_keyword.C
+################################################################################
+sub getVpdTextFileList
+{
+ my($inputVpdTextDir,$prefix) = @_;
+
+ opendir(FILEDIR,$inputVpdTextDir) ||
+ fatal("Couldn't open input vpd text file dir $inputVpdTextDir: $!");
+ # get ALL the vpd files to find the largest vpd decode version
+ my @allVpdTextFiles = grep { /^$prefix.*vpd/ } readdir(FILEDIR);
+ my $largestDecode = "";
+ my $largestDecodeValue = -1;
+ foreach my $vpdFile (@allVpdTextFiles)
+ {
+ my($system,$tarType,$decode,$rest) = split(/\_/,$vpdFile);
+ my $decodeValue = eval($decode);
+ if (undef eq $decodeValue)
+ {
+ warning("decode $decode is not a number: $vpdFile SKIPPED");
+ }
+ if ($decodeValue > $largestDecodeValue)
+ {
+ $largestDecodeValue=$decodeValue;
+ $largestDecode =$decode;
+ }
+ }
+
+ # only get files for the largest attribute decode version
+ rewinddir(FILEDIR);
+ my $prefixDecode = $prefix."_".$largestDecode;
+ my @vpdTextFiles = grep { /^$prefixDecode.*vpd/ }
+ readdir(FILEDIR);
+
+ closedir FILEDIR;
+
+ return @vpdTextFiles
+}
+
+################################################################################
+# Process vpd text file
+#
+# Parsing logic: (example of vpd text file in initial prologue)
+# - keep reading lines as long as $stateKeepReading
+# - process the header, which has the frequrency and rank pair/drop
+# configuration lines
+# - the first target line marks the end of the header.
+# - there can be multiple target lines
+################################################################################
+
+sub processVpdTextFile
+{
+ my($vpdFile) = @_;
+
+ trace("Process file = ".$vpdFile);
+
+ my %config = (); # configuration for current section
+ my %fileInfo = (); # fileInfo for current section
+ my $freqMask = 0; # frequenices (data rates) for this vpd file
+ my $rankMask = 0; # dimm rank count pairs for this vpd file
+ my $mcsMask = 0; # MCS mask for this target
+ my $row = 0; # row count for error message
+
+ # Open vpd text file to parse
+ open(VPDINPUTTEXT,"<$cfgInputVpdTextDir$vpdFile")
+ or fatal("open failed for $cfgInputVpdTextDir$vpdFile: $!");
+
+ # State variables to control parsing the vpd text file
+ my $stateKeepReading = 1; #keep reading lines until EOF
+ my $stateProcHeader = 1; #process header lines until first target line
+ my $stateProcSection = 0; #working on the attributes for a target line
+ my $stateDataRateSet = 0; #Data Rate (frequency) as been found or default
+ my $stateRankConfigSet = 0; #Rank Count (drop) has been found
+
+ while ($stateKeepReading)
+ {
+
+ # Actions to be be performed based on this line of text
+ my $actionDataRate=0;
+ my $actionRankConfig=0;
+ my $actionDropConfig=0;
+ my $actionTarget=0;
+ my $actionAttr=0;
+ my $actionWriteFile=0;
+
+ my $line = <VPDINPUTTEXT>;
+ $row++;
+ if ($line) #determine all actions based on text line
+ {
+ chomp($line);
+ if ($line =~ /ATTR_/) #first since will be the most of these
+ {
+ if ($stateProcHeader)
+ {
+ fatal("ATTR before header complete:\n".
+ " $vpdFile\n".
+ " row $row:$line");
+ }
+ $actionAttr=1;
+ }
+ elsif ($line =~ /DATA_RATE/)
+ {
+ if (!$stateProcHeader)
+ {
+ fatal("DATA_RATE outside of header:\n".
+ " $vpdFile\n".
+ " row $row:$line");
+ }
+ $actionDataRate=1;
+ }
+ elsif ($line =~ /RANK_CONFIG/)
+ {
+ if (!$stateProcHeader)
+ {
+ fatal("RANK_CONFIG outside of header:\n".
+ " $vpdFile\n".
+ " row $row:$line");
+ }
+ $actionRankConfig=1;
+ }
+ elsif ($line =~ /NUM_DROPS/)
+ {
+ if (!$stateProcHeader)
+ {
+ fatal("NUM_DROPS outside of header:\n".
+ " $vpdFile\n".
+ " row $row:$line");
+ }
+ $actionDropConfig=1;
+ }
+ elsif ($line =~ /target/)
+ {
+ # MT files can default to all frequencies if not specificed
+ if ((!$stateDataRateSet) &&
+ ("MT" eq $g_tarType))
+ {
+ $freqMask = FREQ_ALL;
+ trace ("Freq Mask = 0x".sprintf("%02X",$freqMask).
+ " - DEFAULT VALUE");
+ $stateDataRateSet = 1;
+ }
+ if ($stateDataRateSet and $stateRankConfigSet)
+ {
+ $stateProcHeader = 0;
+ $actionTarget=1;
+ }
+ else
+ {
+ fatal("target before header is complete:\n".
+ " $vpdFile\n".
+ " row $row:$line");
+ }
+ if ($stateProcSection)
+ {
+ $actionWriteFile=1; #process previous section before next
+ }
+ }
+ # At this point, everthing of interest has been processed.
+ # The only lines not processed above should be comment lines,
+ # which can be ignored (skipped).
+ }
+ else # determine actions based on hitting end of the text file
+ {
+ $stateKeepReading=0;
+ if ($stateProcSection)
+ {
+ $actionWriteFile=1;
+ }
+ }
+
+ # Process all actions needed based on text line
+ if ($actionDataRate)
+ {
+ $freqMask = procDataRate($line,$vpdFile,$row);
+ $stateDataRateSet = 1;
+ }
+ if ($actionRankConfig)
+ {
+ $rankMask = procRankConfig($line,$vpdFile,$row);
+ $stateRankConfigSet = 1;
+ }
+ if ($actionDropConfig)
+ {
+ $rankMask = procDropConfig($line,$vpdFile,$row);
+ $stateRankConfigSet = 1;
+ }
+ # needs to be before target in case there is a previous target to
+ # finish out before starting the next section.
+ if ($actionWriteFile)
+ {
+ addConfiguration(\%config); #capture final configuration
+ fileWrite(\%fileInfo);
+ }
+ if ($actionTarget)
+ {
+ $mcsMask = procTarget($line,$vpdFile,$row);
+ $stateProcSection = 1;
+ %config = ();
+ %config = newConfiguration($mcsMask,
+ $freqMask,
+ $rankMask,
+ $vpdFile);
+ %fileInfo = ();
+ %fileInfo = newFileInfo($config{CONF_BIN_FILE},
+ VPD_BIN_FILE_SIZE );
+
+ traceConfig("createConfig:",\%config);
+ }
+ if ($actionAttr)
+ {
+ my($attr,$type,$value) = split(/\s+/,$line);
+ my %num = newNum($value,$type);
+ trace(" $attr $type $value bytes=$num{NUM_SIZE}");
+ filePushNum(\%fileInfo,\%num);
+ }
+ }
+}
+
+# process the DATA_RATE line
+#
+# Must be in the 'header' before the first 'target' line
+# # DATA_RATE = XXXX[,XXXX,XXXX,XXXX]
+# at least one frequency, up to 4 frequencies
+sub procDataRate
+{
+ my($line,$file,$row) = @_;
+
+ my $freqMask = 0;
+
+ (undef,my $rest) = split(/\=/,$line);
+
+ my @freqs = split(/\,/,$rest);
+ foreach my $freqText (@freqs)
+ {
+ my $freq =eval($freqText);
+ if (undef eq $freq)
+ {
+ fatal("Invalid freq value = $freqText\n".
+ " $file\n".
+ " row $row: $line");
+ }
+
+ my $found=0;
+ for (my $i=0 ; $i < scalar @g_MrwFreq ; $i++)
+ {
+ if ($freq == $g_MrwFreq[$i])
+ {
+ $freqMask |= $g_freqMask{$i};
+ $found=1;
+ last;
+ }
+ }
+ if (!$found)
+ {
+ fatal("$freq not in MRW freq list\n".
+ " $file\n".
+ " row $row: $line");
+ }
+ }
+ trace ("Freq Mask = 0x".sprintf("%02X",$freqMask));
+ return $freqMask;
+}
+
+# process the RANK_CONFIG line
+#
+# Must be in the 'header' before the first 'target' line
+# # RANK_CONFIG = 0xXX[,0xXX,...,0xXX]
+# X = 0,1,2, or 4
+# at least one dimm count pair, up to 16 pairs
+# the 0xXX format is for reability, its just a number (0x10 same as 16).
+# The first X is dimm0 rank count. Second is dimm1 rank count.
+sub procRankConfig
+{
+ my($line,$file,$row) = @_;
+
+ my $rankMask = 0;
+
+ (undef,my $rest) = split(/\=/,$line);
+
+ my @ranks = split(/\,/,$rest);
+ foreach my $rankText (@ranks)
+ {
+ my $rank =eval($rankText);
+ if (undef eq $rank)
+ {
+ fatal("Invalid rank value = $rankText\n".
+ " $file\n".
+ " row $row: $line");
+ }
+ my $mask = $g_rankMask{$rank};
+ if (undef == $mask)
+ {
+ fatal("Unsupported rank configuration = 0x".
+ sprintf("%02X",$rank)."\n".
+ " $file\n".
+ " row $row: $line");
+ }
+ $rankMask |= $mask;
+ }
+ trace ("Rank Config = 0x".sprintf("%04X",$rankMask));
+ return $rankMask;
+}
+
+# process the NUM_DROPS line
+#
+# Must be in the 'header' before the first 'target' line
+# # NUM_DROPS = X[,X]
+# X = 0 or 1
+sub procDropConfig
+{
+ my($line,$file,$row) = @_;
+
+ my $dropMask = 0;
+
+ (undef,my $rest) = split(/\=/,$line);
+
+ my @drops = split(/\,/,$rest);
+ foreach my $dropText (@drops)
+ {
+ my $drop =eval($dropText);
+ if (undef eq $drop)
+ {
+ fatal("Invalid drop value = $dropText\n".
+ " $file\n".
+ " row $row: $line");
+ }
+ if (1 == $drop)
+ {
+ $dropMask |= RANK_DROP1;
+ }
+ elsif (2 == $drop)
+ {
+ $dropMask |= RANK_DROP2;
+ }
+ else
+ {
+ fatal("Unsupported drop configuration = $drop\n".
+ " $file\n".
+ " row $row: $line");
+ }
+ }
+ trace ("Num Drops Config = 0x".sprintf("%04X",$dropMask));
+ return $dropMask;
+}
+
+# process the target line
+#
+# Concludes the 'header'.
+# target = k0:n0:s0:p9.mcs:pXXX:cXXX
+# XXX = all or a list of comma separated positions (0,1,2 or 3).
+sub procTarget
+{
+ my($line,$file,$row) = @_;
+
+ my $mcsMask = 0;
+
+ (undef,my $rest) = split(/\=/,$line);
+ (undef,undef,undef,my $p9mcs,my $proc,my $chip) = split(/\:/,$line);
+
+ if ( ("p9.mcs" ne $p9mcs) &&
+ ("pu.mcs" ne $p9mcs) &&
+ ("p9n.mcs" ne $p9mcs) )
+ {
+ fatal("Invalid target value = $p9mcs, ".
+ "p9.mcs, pu.mcs or p9n.mcs expected\n".
+ " $file\n".
+ " row $row: $line");
+ }
+
+ my @plist = procPositions($line,$file,$row,$proc,"p");
+ my @clist = procPositions($line,$file,$row,$chip,"c");
+
+ #The MCS bit mask (g_mscMask) is based on the ATTR_MEMVPD_POS of the MCS.
+ #The MEMVPD_POS is the hash key to find the mask value in g_mscMask.
+ #Proc 0 MCS 0 is bit 0x8000. MEMVPD_POS = 0
+ #Proc 0 MCS 1 is bit 0x4000. MEMVPD_POS = 1, ... etc.
+ #MEMVPD_POS can be calculated by proc num * 4 + mcs position within the proc
+ foreach my $ppos(@plist)
+ {
+ foreach my $cpos(@clist)
+ {
+ my $memVpdPos = ($ppos * 4) + $cpos;
+ $mcsMask |= $g_mcsMask{$memVpdPos};
+ }
+ }
+
+ trace("Target Mask = 0x".sprintf("%04X",$mcsMask));
+ return $mcsMask;
+}
+
+# helper to process positions
+# pXXX or cXXX
+# where XXX is "all" or a comma separated list of digits 0,1,2,3
+sub procPositions
+{
+ my($line,$file,$row,$position,$expected) = @_;
+
+ my @list = ();
+
+ my $type = substr($position,0,1);
+ my $rest = substr($position,1);
+ if ($type ne $expected)
+ {
+ fatal("Invalid target specification $type, $expected expected\n".
+ " $file\n".
+ " row $row: $line");
+ }
+
+ if ("ALL" eq uc($rest))
+ {
+ for (my $i=0; $i< MAX_NUM_PROCS ; $i++)
+ {
+ $list[$i]=$i;
+ }
+ }
+ else
+ {
+ my $i = 0;
+ my @positions = split(/\,/,$rest);
+ foreach my $posText (@positions)
+ {
+ my $pos =eval($posText);
+ if (undef eq $pos)
+ {
+ fatal("Invalid position value = $posText\n".
+ " $file\n".
+ " row $row: $line");
+ }
+ if (MAX_POSITION < $pos)
+ {
+ fatal("Position out of 0 to ".MAX_POSITION.
+ " range = $posText\n".
+ " $file\n".
+ " row $row: $line");
+ }
+ $list[$i++]=$pos;
+ }
+ }
+ return @list;
+}
+
+################################################################################
+# File functions
+################################################################################
+
+# create a new file info
+sub newFileInfo
+{
+ my($fileName,$fileSize) = @_;
+
+ my %fileInfo = ();
+ $fileInfo{FILE_NAME}=$fileName;
+ $fileInfo{FILE_SIZE}=$fileSize;
+ $fileInfo{FILE_PTR}=0;
+
+ return %fileInfo;
+}
+
+# append "number" to the end of data to be written
+sub filePushNum
+{
+ my $ref_fileInfo = shift;
+ my $ref_num = shift;
+
+ my $newPtr = $ref_fileInfo->{FILE_PTR} + $ref_num->{NUM_SIZE};
+ if($newPtr > $ref_fileInfo->{FILE_SIZE})
+ {
+ fatal("bin file size exceeded\n".
+ " file=$ref_fileInfo->{FILE_NAME}\n".
+ " limit=$ref_fileInfo->{FILE_SIZE}");
+ }
+ $ref_fileInfo->{FILE_PTR}=$newPtr;
+
+ my $fullValue = $ref_num->{NUM_VALUE};
+ my $byteValue = $fullValue % 256;
+ for (my $i=0 ; $i < $ref_num->{NUM_SIZE} ; $i++)
+ {
+ $newPtr--;
+ $ref_fileInfo->{FILE_DATA}[$newPtr] = $byteValue; #big endian order
+ $fullValue -= $byteValue;
+ $fullValue /= 256;
+ $byteValue = $fullValue % 256;
+ }
+}
+
+# Write the data to the file
+sub fileWrite
+{
+ my $ref_fileInfo = shift;
+
+ my $outBinFile= $cfgOutputVpdBinDir.$ref_fileInfo->{FILE_NAME};
+ open(BIN_FILE,">:raw",$outBinFile) ||
+ fatal("couldn't open $outBinFile: $!");
+
+ for (my $i=0 ; $i<$ref_fileInfo->{FILE_PTR}; $i++)
+ {
+ print BIN_FILE pack('C',$ref_fileInfo->{FILE_DATA}[$i]);
+ }
+ for (my $i=$ref_fileInfo->{FILE_PTR}; $i<$ref_fileInfo->{FILE_SIZE}; $i++)
+ {
+ print BIN_FILE pack('C',0)
+ }
+ close(BIN_FILE);
+ my $pad = $ref_fileInfo->{FILE_SIZE} - $ref_fileInfo->{FILE_PTR};
+
+ trace("create bin file = $ref_fileInfo->{FILE_NAME} ".
+ "data=$ref_fileInfo->{FILE_PTR} pad = $pad");
+}
+
+################################################################################
+# Configuration helpers
+################################################################################
+
+# create a configuration
+my $static_keyChar = undef;
+sub newConfiguration
+{
+ my($mcsMask,$freqMask,$rankMask,$vpdFile) = @_;
+
+ my %config= ();
+ $config{CONF_MCS}=$mcsMask;
+ $config{CONF_FREQ}=$freqMask;
+ $config{CONF_RANK}=$rankMask;
+ $config{CONF_VPD_TEXT_FILE}=$vpdFile;
+
+ if (undef eq $static_keyChar)
+ {
+ $static_keyChar = '0';
+ }
+ elsif ('9' eq $static_keyChar)
+ {
+ $static_keyChar = 'A';
+ }
+ elsif ('Z' eq $static_keyChar)
+ {
+ fatal("Ran out of keyword characters");
+ }
+ else
+ {
+ $static_keyChar++;
+ }
+ $config{CONF_KEY_CHAR}=ord($static_keyChar);
+
+ my $binFileName = "";
+ if ("MR" eq uc $g_tarType)
+ {
+ $binFileName = $cfgPrefix.'_J'.$static_keyChar.".bin";
+ }
+ elsif ("MT" eq uc $g_tarType)
+ {
+ $binFileName = $cfgPrefix.'_X'.$static_keyChar.".bin";
+ }
+ else
+ {
+ fatal("unsupported target type = $g_tarType");
+ }
+ $config{CONF_BIN_FILE}=$binFileName;
+
+ return %config;
+}
+
+# capture a copy of a configuration and add to global list
+sub addConfiguration
+{
+ my $ref_config = shift;
+
+ my %config = %{$ref_config}; #creat a copy
+
+ $g_configs{$config{CONF_KEY_CHAR}}=\%config;
+}
+
+# create mapping bin file and csv
+sub createMappingFile
+{
+ # open file for human readable csv version of mapping data
+ (my $system,my $tarType) = split(/\_/,$cfgPrefix);
+ my $outCsvFile= $cfgOutputVpdBinDir.$cfgPrefix."_map.csv";
+ trace("createMappingFile: $outCsvFile");
+ open(CSV_FILE,">$outCsvFile") ||
+ fatal("Couldn't open $outCsvFile: $!");
+ print CSV_FILE "MCS,RANK,FREQ,KEYCHAR,VPDFILE\n";
+
+ # open file for binary version of mapping data
+ my %fileInfo = newFileInfo($cfgPrefix."_".uc($tarType).".bin",
+ VPD_BIN_FILE_SIZE );
+
+ # start with header
+ # - version
+ # - number of entries
+ # - reserved
+ my %num = newNum(MAPPING_LAYOUT_VERSION,"u8");
+ filePushNum(\%fileInfo,\%num);
+ str2num(\%num,scalar keys %g_configs,"u8");
+ filePushNum(\%fileInfo,\%num);
+ str2num(\%num,0,"u8");
+ filePushNum(\%fileInfo,\%num);
+
+ my @keys = sort {$a <=> $b} keys %g_configs;
+ foreach my $key (@keys)
+ {
+ my $ref_config = $g_configs{$key};
+
+ my $mcsMask = $ref_config->{CONF_MCS};
+ my $freqMask = $ref_config->{CONF_FREQ};
+ my $rankMask = $ref_config->{CONF_RANK};
+ my $keyChar = $ref_config->{CONF_KEY_CHAR};
+ my $vpdFile = $ref_config->{CONF_VPD_TEXT_FILE};
+ my $binFile = $ref_config->{CONF_BIN_FILE};
+
+ print CSV_FILE "0x".sprintf("%04X",$mcsMask).",".
+ "0x".sprintf("%04X",$rankMask).",".
+ "0x".sprintf("%02X",$freqMask).",".
+ "0x".sprintf("%02X",$keyChar).",".
+ chr($keyChar)."-$vpdFile\n";
+
+ #write bin config line
+ # - MCS mask
+ # - Rank mask
+ # - Frequency mask
+ # - keyword character
+ str2num(\%num,$mcsMask,"u16");
+ filePushNum(\%fileInfo,\%num);
+ str2num(\%num,$rankMask,"u16");
+ filePushNum(\%fileInfo,\%num);
+ str2num(\%num,$freqMask,"u8");
+ filePushNum(\%fileInfo,\%num);
+ str2num(\%num,$keyChar,"u8");
+ filePushNum(\%fileInfo,\%num);
+ }
+ fileWrite(\%fileInfo);
+ close(CSV_FILE);
+}
+
+# create report
+sub createReport
+{
+ my $numUnf = 0;
+ my $numOK = 0;
+ my $numDup = 0;
+
+ # open file for report. Report can be used to validate mapping.
+ my $outCsvFile= $cfgOutputVpdBinDir.$cfgPrefix."_report.csv";
+ open(CSV_FILE,">$outCsvFile") ||
+ fatal("Couldn't open $outCsvFile: $!");
+ trace("createReport: $outCsvFile");
+ print CSV_FILE "CHK,MCS,FREQ,RANK,BINFILE,VPDFILE\n";
+
+ my $numMCS = scalar keys %g_mcsMask;
+ my $numFreq = scalar keys %g_freqMask;
+ my $numRank = scalar keys %g_rankMask;
+
+ for (my $i=0 ; $i<$numMCS ; $i++)
+ {
+ for (my $j=0 ; $j<$numFreq ; $j++)
+ {
+ my @keys = sort {$a <=> $b} keys %g_rankMask;
+ foreach my $key (@keys)
+ {
+ my @configList = checkConfig($g_mcsMask{$i},
+ $g_freqMask{$j},
+ $g_rankMask{$key});
+ my $numHits = scalar @configList;
+ my $status = "";
+ if (0 == $numHits)
+ {
+ $status = "UNF";
+ print CSV_FILE "$status,$i,$g_MrwFreq[$j],".
+ "0x".sprintf("%02X",$key).',"",""'."\n";
+ $numUnf++;
+ }
+ else
+ {
+ if (1 == $numHits)
+ {
+ $status = "OK ";
+ $numOK++;
+ }
+ else
+ {
+ $status = "DUP";
+ $numDup++;
+ }
+ for (my $m=0; $m < $numHits ; $m++)
+ {
+ print CSV_FILE "$status,$i,$g_MrwFreq[$j],".
+ "0x".sprintf("%02X",$key).",".
+ "$configList[$m]->{CONF_BIN_FILE},".
+ "$configList[$m]->{CONF_VPD_TEXT_FILE}\n";
+ }
+ }
+ }
+ }
+ }
+ verbose("Of the ".$numMCS*$numFreq*$numRank." combinations:");
+ verbose(" ".sprintf("%4D",$numOK)." are mapped to vpd data");
+ verbose(" ".sprintf("%4D",$numUnf)." are undefined");
+ verbose(" ".sprintf("%4D",$numDup)." are duplicated (should be 0)");
+ if ($numDup)
+ {
+ warning("There are $numDup duplicate mappings, there should be none.\n".
+ " Review $cfgPrefix"."_report.txt and correct the vpd files");
+
+ }
+ close(CSV_FILE);
+}
+
+# Return list of configs that this configuration "hits"
+# Ideally, it is just one or none. A dup means there is a overlap problem
+# in the vpd text files
+sub checkConfig
+{
+ my($mcsMask,$freqMask,$rankMask) = @_;
+
+ my @configList = ();
+ trace("checkConfig:".
+ " mcsMask=".sprintf("%04X",$mcsMask).
+ " freqMask=".sprintf("%02X",$freqMask).
+ " rankMask=".sprintf("%04X",$rankMask));
+
+ my @keys = sort {$a <=> $b} keys %g_configs;
+ foreach my $key (@keys)
+ {
+ my $ref_config = $g_configs{$key};
+ my $status="UNF";
+
+ my $mcsHit = $mcsMask & $ref_config->{CONF_MCS};
+ my $freqHit = $freqMask & $ref_config->{CONF_FREQ};
+ my $rankHit = $rankMask & $ref_config->{CONF_RANK};
+ if ($mcsHit && $freqHit && $rankHit)
+ {
+ push @configList, $ref_config;
+ $status="HIT";
+ }
+ trace(" $status==> ".$ref_config->{CONF_BIN_FILE});
+ trace(" mscHit=0x".sprintf("%04X",$mcsHit).
+ " 0x".sprintf("%04X",$ref_config->{CONF_MCS}));
+ trace(" freqHit=".sprintf("%02X",$freqHit).
+ " 0x".sprintf("%02X",$ref_config->{CONF_FREQ}));
+ trace(" rankHit=".sprintf("%04X",$rankHit).
+ " 0x".sprintf("%04X",$ref_config->{CONF_RANK}));
+ }
+
+ return @configList;
+}
+
+################################################################################
+# Number helpers
+################################################################################
+
+# create a "number" hash
+sub newNum
+{
+ my($value,$type) = @_;
+
+ my ($decSize)= $type =~ /u(\d+)/;
+ my $byteSize = $decSize / 8;
+
+ my %number = ();
+ $number{NUM_VALUE}=str2value($value);
+ $number{NUM_SIZE}=$byteSize;
+
+ return %number;
+}
+
+# update a "number" hash
+sub str2num
+{
+ my $ref_num = shift;
+ my $value = shift;
+ my $type = shift;
+
+ my ($decSize)= $type =~ /u(\d+)/;
+ my $byteSize = $decSize / 8;
+
+ $ref_num->{NUM_VALUE}=str2value($value);
+ $ref_num->{NUM_SIZE}=$byteSize;
+}
+
+# Convert a string into a numeric value scalar
+sub str2value
+{
+ my($text) = @_;
+
+ my $value =eval($text);
+ if ($value eq undef)
+ {
+ fatal("$text is not a number");
+ }
+
+ return $value;
+}
+
+################################################################################
+# trace and related helpers
+################################################################################
+
+# verbose to STDOUT and trace
+sub verbose
+{
+ my($text) = @_;
+
+ if($cfgVerbose)
+ {
+ print STDOUT $text."\n";
+ }
+ trace ($text);
+}
+
+# trace
+my $static_traceInit=0;
+sub trace
+{
+ my($text) = @_;
+
+ if (0 == $static_traceInit)
+ {
+ my $outTextFile= $cfgOutputVpdBinDir.$cfgPrefix."_trace.txt";
+ open(TRACE_FILE,">$outTextFile") ||
+ fatal("couldn't open $outTextFile: $!");
+ $static_traceInit = 1;
+ }
+ print TRACE_FILE $text."\n";
+}
+
+# add a configuration to the trace
+sub traceConfig
+{
+ my($tag,$ref_config) = @_;
+
+ my $mcsMask = $ref_config->{CONF_MCS};
+ my $freqMask = $ref_config->{CONF_FREQ};
+ my $rankMask = $ref_config->{CONF_RANK};
+ my $keyChar = chr($ref_config->{CONF_KEY_CHAR});
+ my $vpdFile = $ref_config->{CONF_VPD_TEXT_FILE};
+ my $binFile = $ref_config->{CONF_BIN_FILE};
+
+ trace("$tag $binFile");
+ trace(" key=$keyChar mcsMask=".sprintf("%04X",$mcsMask).
+ " freqMask=".sprintf("%02X",$freqMask).
+ " rankMask=".sprintf("%04X",$rankMask));
+ trace(" from= $vpdFile");
+}
+
+#warning error
+sub warning
+{
+ my($text) = @_;
+
+ trace("[WARNING!] $text");
+ print STDERR "[WARNING!] $text\n";
+}
+
+
+#fatal error
+sub fatal
+{
+ my($text) = @_;
+
+ trace("[FATAL!] $text");
+ print STDERR "[FATAL!] $text\n";
+
+ exit(1);
+}
+
+# Display the parameters
+sub display_help
+{
+ use File::Basename;
+ my $scriptname = basename($0);
+
+ print STDERR "
+usage:
+ $scriptname --help
+ $scriptname --mrw-MEMVPD-FREQS-MHZ --prefix
+ [--input-vpd-text-dir=./] [--output-vpd-bin-dir=./]
+ [--verbose]
+ --mrw-MEMVPD-FREQS-MHZ
+ Comma separated list of the 4 ATTR_MEMVPD_FREQS_MHZ data rates
+ --prefix
+ Prefix of vpd input files to process (template_mr or template_mt)
+ --input-vpd-text-dir
+ Optional path to directory with input vpd files.
+ Defaults to current directory (./)
+ --output-vpd-bin-dir
+ Optional path to directory for output binary and support files.
+ Defaults to current directory (./)
+ --verbose
+ Optional additional execution information.
+ Defaults to not verbose.
+\n";
+}
OpenPOWER on IntegriCloud