Home | History | Annotate | Download | only in Common
      1 ## @file

      2 # 

      3 # This package manage the VPD PCD information file which will be generated

      4 # by build tool's autogen.

      5 # The VPD PCD information file will be input for third-party BPDG tool which

      6 # is pointed by *_*_*_VPD_TOOL_GUID in conf/tools_def.txt 

      7 #

      8 #

      9 # Copyright (c) 2010 - 2016, Intel Corporation. All rights reserved.<BR>

     10 # This program and the accompanying materials

     11 # are licensed and made available under the terms and conditions of the BSD License

     12 # which accompanies this distribution.  The full text of the license may be found at

     13 # http://opensource.org/licenses/bsd-license.php

     14 #

     15 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

     16 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

     17 #

     18 import Common.LongFilePathOs as os
     19 import re
     20 import Common.EdkLogger as EdkLogger
     21 import Common.BuildToolError as BuildToolError
     22 import subprocess
     23 import Common.GlobalData as GlobalData
     24 from Common.LongFilePathSupport import OpenLongFilePath as open
     25 from Common.Misc import SaveFileOnChange
     26 
     27 FILE_COMMENT_TEMPLATE = \
     28 """
     29 ## @file
     30 #
     31 #  THIS IS AUTO-GENERATED FILE BY BUILD TOOLS AND PLEASE DO NOT MAKE MODIFICATION.
     32 #
     33 #  This file lists all VPD informations for a platform collected by build.exe.
     34 # 
     35 # Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
     36 # This program and the accompanying materials
     37 # are licensed and made available under the terms and conditions of the BSD License
     38 # which accompanies this distribution.  The full text of the license may be found at
     39 # http://opensource.org/licenses/bsd-license.php
     40 #
     41 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     42 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     43 #
     44 
     45 """
     46 
     47 ## The class manage VpdInfoFile.

     48 #

     49 #  This file contains an ordered (based on position in the DSC file) list of the PCDs specified in the platform description file (DSC). The Value field that will be assigned to the PCD comes from the DSC file, INF file (if not defined in the DSC file) or the DEC file (if not defined in the INF file). This file is used as an input to the BPDG tool.

     50 #  Format for this file (using EBNF notation) is:

     51 #  <File>            :: = [<CommentBlock>]

     52 #                         [<PcdEntry>]*

     53 #  <CommentBlock>    ::=  ["#" <String> <EOL>]*

     54 #  <PcdEntry>        ::=  <PcdName> "|" <Offset> "|" <Size> "|" <Value> <EOL>

     55 #  <PcdName>         ::=  <TokenSpaceCName> "." <PcdCName>

     56 #  <TokenSpaceCName> ::=  C Variable Name of the Token Space GUID

     57 #  <PcdCName>        ::=  C Variable Name of the PCD

     58 #  <Offset>          ::=  {"*"} {<HexNumber>}

     59 #  <HexNumber>       ::=  "0x" (a-fA-F0-9){1,8}

     60 #  <Size>            ::=  <HexNumber>

     61 #  <Value>           ::=  {<HexNumber>} {<NonNegativeInt>} {<QString>} {<Array>}

     62 #  <NonNegativeInt>  ::=  (0-9)+

     63 #  <QString>         ::=  ["L"] <DblQuote> <String> <DblQuote>

     64 #  <DblQuote>        ::=  0x22

     65 #  <Array>           ::=  {<CArray>} {<NList>}

     66 #  <CArray>          ::=  "{" <HexNumber> ["," <HexNumber>]* "}"

     67 #  <NList>           ::=  <HexNumber> ["," <HexNumber>]*

     68 #

     69 class VpdInfoFile:
     70     
     71     ## The mapping dictionary from datum type to size string.

     72     _MAX_SIZE_TYPE = {"BOOLEAN":"1", "UINT8":"1", "UINT16":"2", "UINT32":"4", "UINT64":"8"}
     73     _rVpdPcdLine = None 
     74     ## Constructor

     75     def __init__(self):
     76         ## Dictionary for VPD in following format

     77         #

     78         #  Key    : PcdClassObject instance. 

     79         #           @see BuildClassObject.PcdClassObject

     80         #  Value  : offset in different SKU such as [sku1_offset, sku2_offset]

     81         self._VpdArray = {}
     82     
     83     ## Add a VPD PCD collected from platform's autogen when building.

     84     #

     85     #  @param vpds  The list of VPD PCD collected for a platform.

     86     #               @see BuildClassObject.PcdClassObject

     87     #

     88     #  @param offset integer value for VPD's offset in specific SKU.

     89     #

     90     def Add(self, Vpd, Offset):
     91         if (Vpd == None):
     92             EdkLogger.error("VpdInfoFile", BuildToolError.ATTRIBUTE_UNKNOWN_ERROR, "Invalid VPD PCD entry.")
     93         
     94         if not (Offset >= 0 or Offset == "*"):
     95             EdkLogger.error("VpdInfoFile", BuildToolError.PARAMETER_INVALID, "Invalid offset parameter: %s." % Offset)
     96         
     97         if Vpd.DatumType == "VOID*":
     98             if Vpd.MaxDatumSize <= 0:
     99                 EdkLogger.error("VpdInfoFile", BuildToolError.PARAMETER_INVALID, 
    100                                 "Invalid max datum size for VPD PCD %s.%s" % (Vpd.TokenSpaceGuidCName, Vpd.TokenCName))
    101         elif Vpd.DatumType in ["BOOLEAN", "UINT8", "UINT16", "UINT32", "UINT64"]: 
    102             if Vpd.MaxDatumSize == None or Vpd.MaxDatumSize == "":
    103                 Vpd.MaxDatumSize = VpdInfoFile._MAX_SIZE_TYPE[Vpd.DatumType]
    104         else:
    105             EdkLogger.error("VpdInfoFile", BuildToolError.PARAMETER_INVALID,  
    106                             "Invalid DatumType %s for VPD PCD %s.%s" % (Vpd.DatumType, Vpd.TokenSpaceGuidCName, Vpd.TokenCName))
    107             
    108         if Vpd not in self._VpdArray.keys():
    109             #

    110             # If there is no Vpd instance in dict, that imply this offset for a given SKU is a new one 

    111             #

    112             self._VpdArray[Vpd] = [Offset]
    113         else:
    114             #

    115             # If there is an offset for a specific SKU in dict, then append this offset for other sku to array.

    116             #

    117             self._VpdArray[Vpd].append(Offset)
    118             
    119         
    120     ## Generate VPD PCD information into a text file

    121     #  

    122     #  If parameter FilePath is invalid, then assert.

    123     #  If 

    124     #  @param FilePath        The given file path which would hold VPD information

    125     def Write(self, FilePath):
    126         if not (FilePath != None or len(FilePath) != 0):
    127             EdkLogger.error("VpdInfoFile", BuildToolError.PARAMETER_INVALID,  
    128                             "Invalid parameter FilePath: %s." % FilePath)        
    129 
    130         Content = FILE_COMMENT_TEMPLATE
    131         Pcds = self._VpdArray.keys()
    132         Pcds.sort()
    133         for Pcd in Pcds:
    134             i = 0
    135             PcdTokenCName = Pcd.TokenCName
    136             for PcdItem in GlobalData.MixedPcd:
    137                 if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName) in GlobalData.MixedPcd[PcdItem]:
    138                     PcdTokenCName = PcdItem[0]
    139             for Offset in self._VpdArray[Pcd]:
    140                 PcdValue = str(Pcd.SkuInfoList[Pcd.SkuInfoList.keys()[i]].DefaultValue).strip()
    141                 if PcdValue == "" :
    142                     PcdValue  = Pcd.DefaultValue
    143 
    144                 Content += "%s.%s|%s|%s|%s|%s  \n" % (Pcd.TokenSpaceGuidCName, PcdTokenCName, str(Pcd.SkuInfoList.keys()[i]),str(Offset).strip(), str(Pcd.MaxDatumSize).strip(),PcdValue)
    145                 i += 1
    146 
    147         return SaveFileOnChange(FilePath, Content, False)
    148 
    149     ## Read an existing VPD PCD info file.

    150     #

    151     #  This routine will read VPD PCD information from existing file and construct

    152     #  internal PcdClassObject array.

    153     #  This routine could be used by third-party tool to parse VPD info file content.

    154     #

    155     #  @param FilePath The full path string for existing VPD PCD info file.

    156     def Read(self, FilePath):
    157         try:
    158             fd = open(FilePath, "r")
    159         except:
    160             EdkLogger.error("VpdInfoFile", 
    161                             BuildToolError.FILE_OPEN_FAILURE, 
    162                             "Fail to open file %s for written." % FilePath)
    163         Lines = fd.readlines()
    164         for Line in Lines:
    165             Line = Line.strip()
    166             if len(Line) == 0 or Line.startswith("#"):
    167                 continue
    168             
    169             #

    170             # the line must follow output format defined in BPDG spec.

    171             #

    172             try:
    173                 PcdName, SkuId,Offset, Size, Value = Line.split("#")[0].split("|")
    174                 PcdName, SkuId,Offset, Size, Value = PcdName.strip(), SkuId.strip(),Offset.strip(), Size.strip(), Value.strip()
    175                 TokenSpaceName, PcdTokenName = PcdName.split(".")
    176             except:
    177                 EdkLogger.error("BPDG", BuildToolError.PARSER_ERROR, "Fail to parse VPD information file %s" % FilePath)
    178             
    179             Found = False
    180             
    181             for VpdObject in self._VpdArray.keys():
    182                 VpdObjectTokenCName = VpdObject.TokenCName
    183                 for PcdItem in GlobalData.MixedPcd:
    184                     if (VpdObject.TokenCName, VpdObject.TokenSpaceGuidCName) in GlobalData.MixedPcd[PcdItem]:
    185                         VpdObjectTokenCName = PcdItem[0]
    186                 for sku in VpdObject.SkuInfoList.keys():
    187                     if VpdObject.TokenSpaceGuidCName == TokenSpaceName and VpdObjectTokenCName == PcdTokenName.strip() and sku == SkuId:
    188                         if self._VpdArray[VpdObject][VpdObject.SkuInfoList.keys().index(sku)] == "*":
    189                             if Offset == "*":
    190                                 EdkLogger.error("BPDG", BuildToolError.FORMAT_INVALID, "The offset of %s has not been fixed up by third-party BPDG tool." % PcdName)                              
    191                             self._VpdArray[VpdObject][VpdObject.SkuInfoList.keys().index(sku)] = Offset
    192                         Found = True
    193             if not Found:
    194                 EdkLogger.error("BPDG", BuildToolError.PARSER_ERROR, "Can not find PCD defined in VPD guid file.")
    195                 
    196     ## Get count of VPD PCD collected from platform's autogen when building.

    197     #

    198     #  @return The integer count value 

    199     def GetCount(self):
    200         Count = 0
    201         for OffsetList in self._VpdArray.values():
    202             Count += len(OffsetList)
    203             
    204         return Count
    205     
    206     ## Get an offset value for a given VPD PCD

    207     #

    208     #  Because BPDG only support one Sku, so only return offset for SKU default.   

    209     #

    210     #  @param vpd    A given VPD PCD 

    211     def GetOffset(self, vpd):
    212         if not self._VpdArray.has_key(vpd):
    213             return None
    214         
    215         if len(self._VpdArray[vpd]) == 0:
    216             return None
    217         
    218         return self._VpdArray[vpd]
    219     
    220 ## Call external BPDG tool to process VPD file

    221 #    

    222 #  @param ToolPath      The string path name for BPDG tool

    223 #  @param VpdFileName   The string path name for VPD information guid.txt

    224 # 

    225 def CallExtenalBPDGTool(ToolPath, VpdFileName):
    226     assert ToolPath != None, "Invalid parameter ToolPath"
    227     assert VpdFileName != None and os.path.exists(VpdFileName), "Invalid parameter VpdFileName"
    228     
    229     OutputDir = os.path.dirname(VpdFileName)
    230     FileName = os.path.basename(VpdFileName)
    231     BaseName, ext = os.path.splitext(FileName)
    232     OutputMapFileName = os.path.join(OutputDir, "%s.map" % BaseName)
    233     OutputBinFileName = os.path.join(OutputDir, "%s.bin" % BaseName)
    234           
    235     try:
    236         PopenObject = subprocess.Popen(' '.join([ToolPath,
    237                                         '-o', OutputBinFileName, 
    238                                         '-m', OutputMapFileName,
    239                                         '-q',
    240                                         '-f',
    241                                         VpdFileName]),
    242                                         stdout=subprocess.PIPE, 
    243                                         stderr= subprocess.PIPE,
    244                                         shell=True)
    245     except Exception, X:
    246         EdkLogger.error("BPDG", BuildToolError.COMMAND_FAILURE, ExtraData="%s" % (str(X)))
    247     (out, error) = PopenObject.communicate()
    248     print out
    249     while PopenObject.returncode == None :
    250         PopenObject.wait()
    251     
    252     if PopenObject.returncode != 0:
    253         if PopenObject.returncode != 0:
    254             EdkLogger.debug(EdkLogger.DEBUG_1, "Fail to call BPDG tool", str(error))
    255             EdkLogger.error("BPDG", BuildToolError.COMMAND_FAILURE, "Fail to execute BPDG tool with exit code: %d, the error message is: \n %s" % \
    256                             (PopenObject.returncode, str(error)))
    257         
    258     return PopenObject.returncode
    259