]> git.proxmox.com Git - mirror_edk2.git/blobdiff - BaseTools/Source/Python/Common/Misc.py
BaseTools: Supported FMP capsule image.
[mirror_edk2.git] / BaseTools / Source / Python / Common / Misc.py
index e9e41de02e8e1a322c6be1876dfe22c750a486b9..8ba5819cc1e337af0b26f7c7c7f47f6e0c1954cc 100644 (file)
@@ -1,7 +1,7 @@
 ## @file\r
 # Common routines used by all tools\r
 #\r
-# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>\r
+# Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.<BR>\r
 # This program and the accompanying materials\r
 # are licensed and made available under the terms and conditions of the BSD License\r
 # which accompanies this distribution.  The full text of the license may be found at\r
@@ -23,6 +23,8 @@ import time
 import re\r
 import cPickle\r
 import array\r
+import shutil\r
+from struct import pack\r
 from UserDict import IterableUserDict\r
 from UserList import UserList\r
 \r
@@ -43,6 +45,218 @@ gFileTimeStampCache = {}    # {file path : file time stamp}
 ## Dictionary used to store dependencies of files\r
 gDependencyDatabase = {}    # arch : {file path : [dependent files list]}\r
 \r
+def GetVariableOffset(mapfilepath, efifilepath, varnames):\r
+    """ Parse map file to get variable offset in current EFI file \r
+    @param mapfilepath    Map file absolution path\r
+    @param efifilepath:   EFI binary file full path\r
+    @param varnames       iteratable container whose elements are variable names to be searched\r
+    \r
+    @return List whos elements are tuple with variable name and raw offset\r
+    """\r
+    lines = []\r
+    try:\r
+        f = open(mapfilepath, 'r')\r
+        lines = f.readlines()\r
+        f.close()\r
+    except:\r
+        return None\r
+    \r
+    if len(lines) == 0: return None\r
+    firstline = lines[0].strip()\r
+    if (firstline.startswith("Archive member included ") and\r
+        firstline.endswith(" file (symbol)")):\r
+        return _parseForGCC(lines, efifilepath, varnames)\r
+    return _parseGeneral(lines, efifilepath, varnames)\r
+\r
+def _parseForGCC(lines, efifilepath, varnames):\r
+    """ Parse map file generated by GCC linker """\r
+    status = 0\r
+    sections = []\r
+    varoffset = []\r
+    for line in lines:\r
+        line = line.strip()\r
+        # status machine transection\r
+        if status == 0 and line == "Memory Configuration":\r
+            status = 1\r
+            continue\r
+        elif status == 1 and line == 'Linker script and memory map':\r
+            status = 2\r
+            continue\r
+        elif status ==2 and line == 'START GROUP':\r
+            status = 3\r
+            continue\r
+\r
+        # status handler\r
+        if status == 2:\r
+            m = re.match('^([\w_\.]+) +([\da-fA-Fx]+) +([\da-fA-Fx]+)$', line)\r
+            if m != None:\r
+                sections.append(m.groups(0))\r
+            for varname in varnames:\r
+                m = re.match("^([\da-fA-Fx]+) +[_]*(%s)$" % varname, line)\r
+                if m != None:\r
+                    varoffset.append((varname, int(m.groups(0)[0], 16) , int(sections[-1][1], 16), sections[-1][0]))\r
+\r
+    if not varoffset:\r
+        return []\r
+    # get section information from efi file\r
+    efisecs = PeImageClass(efifilepath).SectionHeaderList\r
+    if efisecs == None or len(efisecs) == 0:\r
+        return []\r
+    #redirection\r
+    redirection = 0\r
+    for efisec in efisecs:\r
+        for section in sections:\r
+            if section[0].strip() == efisec[0].strip() and section[0].strip() == '.text':\r
+                redirection = int(section[1], 16) - efisec[1]\r
+\r
+    ret = []\r
+    for var in varoffset:\r
+        for efisec in efisecs:\r
+            if var[1] >= efisec[1] and var[1] < efisec[1]+efisec[3]:\r
+                ret.append((var[0], hex(efisec[2] + var[1] - efisec[1] - redirection)))\r
+    return ret\r
+\r
+def _parseGeneral(lines, efifilepath, varnames):\r
+    status = 0    #0 - beginning of file; 1 - PE section definition; 2 - symbol table\r
+    secs  = []    # key = section name\r
+    varoffset = []\r
+    secRe = re.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\da-fA-F]+)[Hh]? +([.\w\$]+) +(\w+)', re.UNICODE)\r
+    symRe = re.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\.:\\\\\w\?@\$]+) +([\da-fA-F]+)', re.UNICODE)\r
+\r
+    for line in lines:\r
+        line = line.strip()\r
+        if re.match("^Start[' ']+Length[' ']+Name[' ']+Class", line):\r
+            status = 1\r
+            continue\r
+        if re.match("^Address[' ']+Publics by Value[' ']+Rva\+Base", line):\r
+            status = 2\r
+            continue\r
+        if re.match("^entry point at", line):\r
+            status = 3\r
+            continue        \r
+        if status == 1 and len(line) != 0:\r
+            m =  secRe.match(line)\r
+            assert m != None, "Fail to parse the section in map file , line is %s" % line\r
+            sec_no, sec_start, sec_length, sec_name, sec_class = m.groups(0)\r
+            secs.append([int(sec_no, 16), int(sec_start, 16), int(sec_length, 16), sec_name, sec_class])\r
+        if status == 2 and len(line) != 0:\r
+            for varname in varnames:\r
+                m = symRe.match(line)\r
+                assert m != None, "Fail to parse the symbol in map file, line is %s" % line\r
+                sec_no, sym_offset, sym_name, vir_addr = m.groups(0)\r
+                sec_no     = int(sec_no,     16)\r
+                sym_offset = int(sym_offset, 16)\r
+                vir_addr   = int(vir_addr,   16)\r
+                m2 = re.match('^[_]*(%s)' % varname, sym_name)\r
+                if m2 != None:\r
+                    # fond a binary pcd entry in map file\r
+                    for sec in secs:\r
+                        if sec[0] == sec_no and (sym_offset >= sec[1] and sym_offset < sec[1] + sec[2]):\r
+                            varoffset.append([varname, sec[3], sym_offset, vir_addr, sec_no])\r
+\r
+    if not varoffset: return []\r
+\r
+    # get section information from efi file\r
+    efisecs = PeImageClass(efifilepath).SectionHeaderList\r
+    if efisecs == None or len(efisecs) == 0:\r
+        return []\r
+\r
+    ret = []\r
+    for var in varoffset:\r
+        index = 0\r
+        for efisec in efisecs:\r
+            index = index + 1\r
+            if var[1].strip() == efisec[0].strip():\r
+                ret.append((var[0], hex(efisec[2] + var[2])))\r
+            elif var[4] == index:\r
+                ret.append((var[0], hex(efisec[2] + var[2])))\r
+\r
+    return ret\r
+\r
+## Routine to process duplicated INF\r
+#\r
+#  This function is called by following two cases:\r
+#  Case 1 in DSC:\r
+#    [components.arch]\r
+#    Pkg/module/module.inf\r
+#    Pkg/module/module.inf {\r
+#      <Defines>\r
+#        FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836\r
+#    }\r
+#  Case 2 in FDF:\r
+#    INF Pkg/module/module.inf\r
+#    INF FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836 Pkg/module/module.inf\r
+#\r
+#  This function copies Pkg/module/module.inf to\r
+#  Conf/.cache/0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf\r
+#\r
+#  @param Path     Original PathClass object\r
+#  @param BaseName New file base name\r
+#\r
+#  @retval         return the new PathClass object\r
+#\r
+def ProcessDuplicatedInf(Path, BaseName, Workspace):\r
+    Filename = os.path.split(Path.File)[1]\r
+    if '.' in Filename:\r
+        Filename = BaseName + Path.BaseName + Filename[Filename.rfind('.'):]\r
+    else:\r
+        Filename = BaseName + Path.BaseName\r
+\r
+    #\r
+    # If -N is specified on command line, cache is disabled\r
+    # The directory has to be created\r
+    #\r
+    DbDir = os.path.split(GlobalData.gDatabasePath)[0]\r
+    if not os.path.exists(DbDir):\r
+        os.makedirs(DbDir)\r
+    #\r
+    # A temporary INF is copied to database path which must have write permission\r
+    # The temporary will be removed at the end of build\r
+    # In case of name conflict, the file name is \r
+    # FILE_GUIDBaseName (0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf)\r
+    #\r
+    TempFullPath = os.path.join(DbDir,\r
+                                Filename)\r
+    RtPath = PathClass(Path.File, Workspace)\r
+    #\r
+    # Modify the full path to temporary path, keep other unchanged\r
+    #\r
+    # To build same module more than once, the module path with FILE_GUID overridden has\r
+    # the file name FILE_GUIDmodule.inf, but the relative path (self.MetaFile.File) is the real path\r
+    # in DSC which is used as relative path by C files and other files in INF. \r
+    # A trick was used: all module paths are PathClass instances, after the initialization\r
+    # of PathClass, the PathClass.Path is overridden by the temporary INF path.\r
+    #\r
+    # The reason for creating a temporary INF is:\r
+    # Platform.Modules which is the base to create ModuleAutoGen objects is a dictionary,\r
+    # the key is the full path of INF, the value is an object to save overridden library instances, PCDs.\r
+    # A different key for the same module is needed to create different output directory,\r
+    # retrieve overridden PCDs, library instances.\r
+    #\r
+    # The BaseName is the FILE_GUID which is also the output directory name.\r
+    #\r
+    #\r
+    RtPath.Path = TempFullPath\r
+    RtPath.BaseName = BaseName\r
+    #\r
+    # If file exists, compare contents\r
+    #\r
+    if os.path.exists(TempFullPath):\r
+        with open(str(Path), 'rb') as f1: Src = f1.read()\r
+        with open(TempFullPath, 'rb') as f2: Dst = f2.read()\r
+        if Src == Dst:\r
+            return RtPath\r
+    GlobalData.gTempInfs.append(TempFullPath)\r
+    shutil.copy2(str(Path), TempFullPath)\r
+    return RtPath\r
+\r
+## Remove temporary created INFs whose paths were saved in gTempInfs\r
+#\r
+def ClearDuplicatedInf():\r
+    for File in GlobalData.gTempInfs:\r
+        if os.path.exists(File):\r
+            os.remove(File)\r
+\r
 ## callback routine for processing variable option\r
 #\r
 # This function can be used to process variable number of option values. The\r
@@ -441,6 +655,7 @@ def RealPath(File, Dir='', OverrideDir=''):
     return NewFile\r
 \r
 def RealPath2(File, Dir='', OverrideDir=''):\r
+    NewFile = None\r
     if OverrideDir:\r
         NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(OverrideDir, File))]\r
         if NewFile:\r
@@ -450,8 +665,10 @@ def RealPath2(File, Dir='', OverrideDir=''):
                 return NewFile[len(OverrideDir)+1:], NewFile[0:len(OverrideDir)]\r
     if GlobalData.gAllFiles:\r
         NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(Dir, File))]\r
-    else:\r
+    if not NewFile:\r
         NewFile = os.path.normpath(os.path.join(Dir, File))\r
+        if not os.path.exists(NewFile):\r
+            return None, None\r
     if NewFile:\r
         if Dir:\r
             if Dir[-1] == os.path.sep:\r
@@ -1292,15 +1509,17 @@ def AnalyzeDscPcd(Setting, PcdType, DataType=''):
         return [VpdOffset, Size, Value], IsValid, 2\r
     elif PcdType in (MODEL_PCD_DYNAMIC_HII, MODEL_PCD_DYNAMIC_EX_HII):\r
         HiiString = FieldList[0]\r
-        Guid = Offset = Value = ''\r
+        Guid = Offset = Value = Attribute = ''\r
         if len(FieldList) > 1:\r
             Guid = FieldList[1]\r
         if len(FieldList) > 2:\r
             Offset = FieldList[2]\r
         if len(FieldList) > 3:\r
             Value = FieldList[3]\r
-        IsValid = (3 <= len(FieldList) <= 4)\r
-        return [HiiString, Guid, Offset, Value], IsValid, 3\r
+        if len(FieldList) > 4:\r
+            Attribute = FieldList[4]\r
+        IsValid = (3 <= len(FieldList) <= 5)\r
+        return [HiiString, Guid, Offset, Value, Attribute], IsValid, 3\r
     return [], False, 0\r
 \r
 ## AnalyzePcdData\r
@@ -1456,6 +1675,45 @@ def CommonPath(PathList):
             return os.path.sep.join(P1[:Index])\r
     return os.path.sep.join(P1)\r
 \r
+#\r
+# Convert string to C format array\r
+#\r
+def ConvertStringToByteArray(Value):\r
+    Value = Value.strip()\r
+    if not Value:\r
+        return None\r
+    if Value[0] == '{':\r
+        if not Value.endswith('}'):\r
+            return None\r
+        Value = Value.replace(' ', '').replace('{', '').replace('}', '')\r
+        ValFields = Value.split(',')\r
+        try:\r
+            for Index in range(len(ValFields)):\r
+                ValFields[Index] = str(int(ValFields[Index], 0))\r
+        except ValueError:\r
+            return None\r
+        Value = '{' + ','.join(ValFields) + '}'\r
+        return Value\r
+\r
+    Unicode = False\r
+    if Value.startswith('L"'):\r
+        if not Value.endswith('"'):\r
+            return None\r
+        Value = Value[1:]\r
+        Unicode = True\r
+    elif not Value.startswith('"') or not Value.endswith('"'):\r
+        return None\r
+\r
+    Value = eval(Value)         # translate escape character\r
+    NewValue = '{'\r
+    for Index in range(0,len(Value)):\r
+        if Unicode:\r
+            NewValue = NewValue + str(ord(Value[Index]) % 0x10000) + ','\r
+        else:\r
+            NewValue = NewValue + str(ord(Value[Index]) % 0x100) + ','\r
+    Value = NewValue + '0}'\r
+    return Value\r
+\r
 class PathClass(object):\r
     def __init__(self, File='', Root='', AlterRoot='', Type='', IsBinary=False,\r
                  Arch='COMMON', ToolChainFamily='', Target='', TagName='', ToolCode=''):\r
@@ -1698,17 +1956,26 @@ class SkuClass():
         \r
         self.AvailableSkuIds = sdict()\r
         self.SkuIdSet = []\r
-        \r
+        self.SkuIdNumberSet = []\r
         if SkuIdentifier == '' or SkuIdentifier is None:\r
             self.SkuIdSet = ['DEFAULT']\r
+            self.SkuIdNumberSet = ['0U']\r
         elif SkuIdentifier == 'ALL':\r
             self.SkuIdSet = SkuIds.keys()\r
+            self.SkuIdNumberSet = [num.strip() + 'U' for num in SkuIds.values()]\r
         else:\r
             r = SkuIdentifier.split('|') \r
             self.SkuIdSet=[r[k].strip() for k in range(len(r))]      \r
+            k = None\r
+            try: \r
+                self.SkuIdNumberSet = [SkuIds[k].strip() + 'U' for k in self.SkuIdSet]   \r
+            except Exception:\r
+                EdkLogger.error("build", PARAMETER_INVALID,\r
+                            ExtraData = "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"\r
+                                      % (k, " ".join(SkuIds.keys())))\r
         if len(self.SkuIdSet) == 2 and 'DEFAULT' in self.SkuIdSet and SkuIdentifier != 'ALL':\r
             self.SkuIdSet.remove('DEFAULT')\r
-                \r
+            self.SkuIdNumberSet.remove('0U')\r
         for each in self.SkuIdSet:\r
             if each in SkuIds:\r
                 self.AvailableSkuIds[each] = SkuIds[each]\r
@@ -1735,10 +2002,31 @@ class SkuClass():
             return self.SkuIdSet[0]\r
         else:\r
             return 'DEFAULT'\r
-            \r
+    def __GetAvailableSkuIdNumber(self):\r
+        return self.SkuIdNumberSet\r
     SystemSkuId = property(__GetSystemSkuID)\r
     AvailableSkuIdSet = property(__GetAvailableSkuIds)\r
     SkuUsageType = property(__SkuUsageType)\r
+    AvailableSkuIdNumSet = property(__GetAvailableSkuIdNumber)\r
+\r
+#\r
+# Pack a registry format GUID\r
+#\r
+def PackRegistryFormatGuid(Guid):\r
+    Guid = Guid.split('-')\r
+    return pack('=LHHBBBBBBBB',\r
+                int(Guid[0], 16),\r
+                int(Guid[1], 16),\r
+                int(Guid[2], 16),\r
+                int(Guid[3][-4:-2], 16),\r
+                int(Guid[3][-2:], 16),\r
+                int(Guid[4][-12:-10], 16),\r
+                int(Guid[4][-10:-8], 16),\r
+                int(Guid[4][-8:-6], 16),\r
+                int(Guid[4][-6:-4], 16),\r
+                int(Guid[4][-4:-2], 16),\r
+                int(Guid[4][-2:], 16)\r
+                )\r
 \r
 ##\r
 #\r