Import EOT tool if only required
[efi/basetools/.git] / Source / Python / build / BuildReport.py
index 71b9e02..cb56ca2 100755 (executable)
@@ -20,7 +20,11 @@ import os
 import re
 import platform
 import textwrap
+from datetime import datetime
 from Common import EdkLogger
+from Common.Misc import GuidStructureByteArrayToGuidString
+from Common.Misc import GuidStructureStringToGuidString
+from Common.InfClassObject import gComponentType2ModuleType
 from Common.BuildToolError import FILE_OPEN_FAILURE
 from Common.BuildToolError import FILE_WRITE_FAILURE
 
@@ -32,12 +36,27 @@ gDxsDependencyPattern = re.compile(r"DEPENDENCY_START(.+)DEPENDENCY_END", re.DOT
 gFvTotalSizePattern = re.compile(r"EFI_FV_TOTAL_SIZE = (0x[0-9a-fA-F]+)")
 gFvTakenSizePattern = re.compile(r"EFI_FV_TAKEN_SIZE = (0x[0-9a-fA-F]+)")
 
+## Pattern to find module size and time stamp in module summary report intermediate file  
+gModuleSizePattern = re.compile(r"MODULE_SIZE = (\d+)")
+gTimeStampPattern  = re.compile(r"TIME_STAMP = (\d+)") 
+
 ## Pattern to find GUID value in flash description files
 gPcdGuidPattern = re.compile(r"PCD\((\w+)[.](\w+)\)")
 
 ## Pattern to collect offset, GUID value pair in the flash report intermediate file 
 gOffsetGuidPattern = re.compile(r"(0x[0-9A-Fa-f]+) ([-A-Fa-f0-9]+)")
 
+## Pattern to find module base address and entry point in fixed flash map file
+gModulePattern = r"\n\w+\s*\(([^,]+),\s*BaseAddress=%(Address)s,\s*EntryPoint=%(Address)s\)\s*\(GUID=([-0-9A-Fa-f]+)[^)]*\)"
+gMapFileItemPattern = re.compile(gModulePattern % {"Address" : "(-?0[xX][0-9A-Fa-f]+)"})
+
+## Pattern to find all module referenced header files in source files
+gIncludePattern  = re.compile(r'#include\s*["<]([^">]+)[">]')
+gIncludePattern2 = re.compile(r"#include\s+EFI_([A-Z_]+)\s*[(]\s*(\w+)\s*[)]")
+
+## Pattern to find the entry point for EDK module using EDKII Glue library
+gGlueLibEntryPoint = re.compile(r"__EDKII_GLUE_MODULE_ENTRY_POINT__\s*=\s*(\w+)")
+
 ## Tags for section start, end and separator
 gSectionStart = ">" + "=" * 118 + "<"
 gSectionEnd = "<" + "=" * 118 + ">" + "\n"
@@ -61,6 +80,21 @@ gPcdTypeMap = {
   'DynamicExVpd'     : ('DEXVPD', 'Dynamic'),
   }
 
+## The look up table to map module type to driver type
+gDriverTypeMap = {
+  'SEC'               : '0x3 (SECURITY_CORE)',
+  'PEI_CORE'          : '0x4 (PEI_CORE)',
+  'PEIM'              : '0x6 (PEIM)',
+  'DXE_CORE'          : '0x5 (DXE_CORE)',
+  'DXE_DRIVER'        : '0x7 (DRIVER)',
+  'DXE_SAL_DRIVER'    : '0x7 (DRIVER)',
+  'DXE_SMM_DRIVER'    : '0x7 (DRIVER)',
+  'DXE_RUNTIME_DRIVER': '0x7 (DRIVER)',
+  'UEFI_DRIVER'       : '0x7 (DRIVER)',
+  'UEFI_APPLICATION'  : '0x9 (APPLICATION)',
+  'SMM_CORE'          : '0xD (SMM_CORE)',
+  }
+
 ##
 # Writes a string to the file object.
 # 
@@ -74,9 +108,54 @@ gPcdTypeMap = {
 def FileWrite(File, String, Wrapper=False):
     if Wrapper:
         String = textwrap.fill(String, 120)
-    File.write(String + "\n")
-
+    File.write(String + "\r\n")
 
+##
+# Find all the header file that the module source directly includes.
+#
+# This function scans source code to find all header files the module may
+# include. This is not accurate but very effective to find all the header
+# file the module might include with #include statement. 
+#
+# @Source                    The source file name
+# @IncludePathList           The list of include path to find the source file.
+# @IncludeFiles              The dictionary of current found include files.
+#
+def FindIncludeFiles(Source, IncludePathList, IncludeFiles):
+    FileContents = open(Source).read()
+    #
+    # Find header files with pattern #include "XXX.h" or #include <XXX.h>
+    #
+    for Match in gIncludePattern.finditer(FileContents):
+        FileName = Match.group(1).strip()
+        for Dir in [os.path.dirname(Source)] + IncludePathList:
+            FullFileName = os.path.normpath(os.path.join(Dir, FileName))
+            if os.path.exists(FullFileName):
+                IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
+                break
+    
+    #
+    # Find header files with pattern like #include EFI_PPI_CONSUMER(XXX)
+    #
+    for Match in gIncludePattern2.finditer(FileContents):
+        Key = Match.group(2)
+        Type = Match.group(1)
+        if "ARCH_PROTOCOL" in Type:
+            FileName = "ArchProtocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
+        elif "PROTOCOL" in Type:
+            FileName = "Protocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
+        elif "PPI" in Type:
+            FileName = "Ppi/%(Key)s/%(Key)s.h" % {"Key" : Key}
+        elif "GUID" in Type:
+            FileName = "Guid/%(Key)s/%(Key)s.h" % {"Key" : Key}
+        else:
+            continue
+        for Dir in IncludePathList:
+            FullFileName = os.path.normpath(os.path.join(Dir, FileName))
+            if os.path.exists(FullFileName):
+                IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
+                break
+            
 ##
 # Reports library information
 #
@@ -286,7 +365,8 @@ class BuildFlagsReport(object):
             FileWrite(File, "%s = %s" % (Tool, self.BuildFlags[Tool]), True)
         
         FileWrite(File, gSubSectionEnd)
-                       
+
+
 ##
 # Reports individual module information
 #
@@ -311,14 +391,19 @@ class ModuleReport(object):
         self.ModuleInfPath = M.MetaFile.File
         self.FileGuid = M.Guid
         self.Size = 0
-        self.BuildTimeStamp = ""
+        self.BuildTimeStamp = None
         self.DriverType = ""
+        ModuleType = M.ModuleType
+        if not ModuleType:
+            ModuleType = gComponentType2ModuleType.get(M.ComponentType, "")
+        self.DriverType = gDriverTypeMap.get(ModuleType, "")
         self.UefiSpecVersion = M.Module.Specification.get("UEFI_SPECIFICATION_VERSION", "")
         self.PiSpecVersion = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "")
         self.PciDeviceId = M.Module.Defines.get("PCI_DEVICE_ID", "")
         self.PciVendorId = M.Module.Defines.get("PCI_VENDOR_ID", "")
         self.PciClassCode = M.Module.Defines.get("PCI_CLASS_CODE", "")
   
+        self._BuildDir = M.BuildDir
         self.ModulePcdSet = {}
         self.ModuleDscOverridePcds = {}
         if "PCD" in ReportType:
@@ -346,6 +431,7 @@ class ModuleReport(object):
         
         if "BUILD_FLAGS" in ReportType:
             self.BuildFlagsReport = BuildFlagsReport(M)
+        
     
     ##
     # Generate report for module information
@@ -358,15 +444,29 @@ class ModuleReport(object):
     # @param GlobalPcdReport The platform global PCD class object
     # @param ReportType      The kind of report items in the final report file
     #
-    def GenerateReport(self, File, GlobalPcdReport, ReportType):
+    def GenerateReport(self, File, GlobalPcdReport, GlobalPredictionReport, ReportType):
         FileWrite(File, gSectionStart)
         
+        FwReportFileName = os.path.join(self._BuildDir, "DEBUG", self.ModuleName + ".txt")
+        if os.path.isfile(FwReportFileName):
+            try:
+                FileContents = open(FwReportFileName).read()
+                Match = gModuleSizePattern.search(FileContents)
+                if Match:
+                    self.Size = int(Match.group(1))
+                
+                Match = gTimeStampPattern.search(FileContents)
+                if Match:
+                    self.BuildTimeStamp = datetime.utcfromtimestamp(int(Match.group(1)))
+            except IOError:
+                EdkLogger.warn(None, "Fail to read report file", FwReportFileName)
+            
         FileWrite(File, "Module Summary")
         FileWrite(File, "Module Name:          %s" % self.ModuleName)
         FileWrite(File, "Module INF Path:      %s" % self.ModuleInfPath)
         FileWrite(File, "File GUID:            %s" % self.FileGuid)
         if self.Size:
-            FileWrite(File, "Size:                 %s" % self.Size)
+            FileWrite(File, "Size:                 0x%X (%.2fK)" % (self.Size, self.Size / 1024.0))
         if self.BuildTimeStamp:
             FileWrite(File, "Build Time Stamp:     %s" % self.BuildTimeStamp)
         if self.DriverType:
@@ -395,7 +495,10 @@ class ModuleReport(object):
         
         if "BUILD_FLAGS" in ReportType:
             self.BuildFlagsReport.GenerateReport(File)
-        
+
+        if "PREDICTION" in ReportType:
+            GlobalPredictionReport.GenerateReport(File, self.FileGuid)
+    
         FileWrite(File, gSectionEnd)
 
 ##
@@ -418,7 +521,10 @@ class PcdReport(object):
     def __init__(self, Wa):
         self.AllPcds = {}
         self.MaxLen = 0
-        self.FdfPcdSet = Wa.FdfProfile.PcdDict
+        if Wa.FdfProfile:
+            self.FdfPcdSet = Wa.FdfProfile.PcdDict
+        else:
+            self.FdfPcdSet = {}
 
         self.DecPcdDefault = {}
         self.ModulePcdOverride = {}
@@ -597,6 +703,543 @@ class PcdReport(object):
             FileWrite(File, gSubSectionEnd)     
 
 
+
+##
+# Reports platform and module Prediction information
+#
+# This class reports the platform execution order prediction section and
+# module load fixed address prediction subsection in the build report file.
+#    
+class PredictionReport(object):
+    ##
+    # Constructor function for class PredictionReport
+    #
+    # This constructor function generates PredictionReport object for the platform. 
+    #
+    # @param self:           The object pointer
+    # @param Wa              Workspace context information
+    #
+    def __init__(self, Wa):
+        self._MapFileName = os.path.join(Wa.BuildDir, Wa.Name + ".map")
+        self._MapFileParsed = False
+        self._EotToolInvoked = False
+        self._FvDir = Wa.FvDir
+        self._EotDir = Wa.BuildDir
+        self._FfsEntryPoint = {}
+        self._GuidMap = {}
+        self._SourceList = []
+        self.FixedMapDict = {}
+        self.ItemList = []
+        self.MaxLen = 0
+       
+        #
+        # Collect all platform reference source files and GUID C Name
+        #
+        for Pa in Wa.AutoGenObjectList:
+            for Module in Pa.LibraryAutoGenList + Pa.ModuleAutoGenList:
+                #
+                # Add module referenced source files
+                #
+                self._SourceList.append(str(Module))
+                IncludeList = {}
+                for Source in Module.SourceFileList:
+                    if os.path.splitext(str(Source))[1].lower() == ".c":
+                        self._SourceList.append("  " + str(Source))
+                        FindIncludeFiles(Source.Path, Module.IncludePathList, IncludeList)
+                for IncludeFile in IncludeList.values():
+                    self._SourceList.append("  " + IncludeFile)
+                for Guid in Module.PpiList:
+                    self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.PpiList[Guid])
+                for Guid in Module.ProtocolList:
+                    self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.ProtocolList[Guid])
+                for Guid in Module.GuidList:
+                    self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.GuidList[Guid])
+        
+                if Module.Guid and not Module.IsLibrary:
+                    EntryPoint = " ".join(Module.Module.ModuleEntryPointList)
+                    if int(str(Module.AutoGenVersion), 0) >= 0x00010005:
+                        RealEntryPoint = "_ModuleEntryPoint"
+                    else:
+                        RealEntryPoint = EntryPoint
+                        if EntryPoint == "_ModuleEntryPoint":
+                            CCFlags = Module.BuildOption.get("CC", {}).get("FLAGS", "")
+                            Match = gGlueLibEntryPoint.search(CCFlags)
+                            if Match:
+                                EntryPoint = Match.group(1)
+                                
+                    self._FfsEntryPoint[Module.Guid.upper()] = (EntryPoint, RealEntryPoint)
+                         
+  
+        #
+        # Collect platform firmware volume list as the input of EOT.
+        #
+        self._FvList = []
+        if Wa.FdfProfile: 
+            for Fd in Wa.FdfProfile.FdDict:
+                for FdRegion in Wa.FdfProfile.FdDict[Fd].RegionList:
+                    if FdRegion.RegionType != "FV":
+                        continue
+                    for FvName in FdRegion.RegionDataList:
+                        if FvName in self._FvList:
+                            continue
+                        self._FvList.append(FvName)
+                        for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
+                            for Section in Ffs.SectionList:
+                                try:
+                                    for FvSection in Section.SectionList:
+                                        if FvSection.FvName in self._FvList:
+                                            continue
+                                        self._FvList.append(FvSection.FvName)
+                                except AttributeError:
+                                    pass
+        
+
+    ##
+    # Parse platform fixed address map files
+    #
+    # This function parses the platform final fixed address map file to get
+    # the database of predicted fixed address for module image base, entry point
+    # etc. 
+    #
+    # @param self:           The object pointer
+    #  
+    def _ParseMapFile(self):
+        if self._MapFileParsed:
+            return
+        self._MapFileParsed = True
+        if os.path.isfile(self._MapFileName):
+            try:
+                FileContents = open(self._MapFileName).read()
+                for Match in gMapFileItemPattern.finditer(FileContents):
+                    AddressType = Match.group(1)
+                    BaseAddress = Match.group(2)
+                    EntryPoint = Match.group(3)
+                    Guid = Match.group(4).upper()
+                    List = self.FixedMapDict.setdefault(Guid, [])
+                    List.append((AddressType, BaseAddress, "*I"))
+                    List.append((AddressType, EntryPoint, "*E"))
+            except:
+                EdkLogger.warn(None, "Cannot open file to read", self._MapFileName)
+    
+    ##
+    # Invokes EOT tool to get the predicted the execution order.
+    #
+    # This function invokes EOT tool to calculate the predicted dispatch order
+    #
+    # @param self:           The object pointer
+    #  
+    def _InvokeEotTool(self):
+        if self._EotToolInvoked:
+            return
+        
+        self._EotToolInvoked = True        
+        FvFileList = []
+        for FvName in self._FvList:
+            FvFile = os.path.join(self._FvDir, FvName + ".Fv")
+            if os.path.isfile(FvFile):
+                FvFileList.append(FvFile)
+       
+        #
+        # Write source file list and GUID file list to an intermediate file
+        # as the input for EOT tool and dispatch List as the output file
+        # from EOT tool.
+        #
+        SourceList = os.path.join(self._EotDir, "SourceFile.txt")
+        GuidList = os.path.join(self._EotDir, "GuidList.txt")
+        DispatchList = os.path.join(self._EotDir, "Dispatch.txt")
+        
+        TempFile = open(SourceList, "w+")
+        for Item in self._SourceList:
+            FileWrite(TempFile, Item)
+        TempFile.close()
+        TempFile = open(GuidList, "w+")
+        for Key in self._GuidMap:
+            FileWrite(TempFile, "%s %s" % (Key, self._GuidMap[Key]))
+        TempFile.close()
+        
+        try:
+            from Eot.Eot import Eot
+            #
+            # Invoke EOT tool
+            #
+            Eot(CommandLineOption=False, SourceFileList=SourceList, GuidList=GuidList,
+                    FvFileList=' '.join(FvFileList), Dispatch=DispatchList, IsInit=True)
+                
+            #
+            # Parse the output of EOT tool
+            #
+            for Line in open(DispatchList):
+                (Guid, Phase, FfsName, FilePath) = Line.split()
+                Symbol = self._FfsEntryPoint.get(Guid, [FfsName, ""])[0]
+                if len(Symbol) > self.MaxLen:
+                    self.MaxLen = len(Symbol)
+                self.ItemList.append((Phase, Symbol, FilePath))
+        except:
+            EdkLogger.warn(None,  "Failed to generate execution order prediction report, \
+                                  for some error occurred in executing EOT.") 
+
+   
+    ##
+    # Generate platform execution order report
+    #
+    # This function generates the predicted module execution order.
+    #
+    # @param self            The object pointer
+    # @param File            The file object for report
+    #
+    def _GenerateExecutionOrderReport(self, File):
+        FileWrite(File, gSectionStart)
+        FileWrite(File, "Execution Order Prediction")
+        FileWrite(File, "*P PEI phase")
+        FileWrite(File, "*D DXE phase")
+        FileWrite(File, "*E Module INF entry point name")
+        FileWrite(File, "*N Module notification function name")
+        
+        FileWrite(File, "Type %-*s %s" % (self.MaxLen, "Symbol", "Module INF Path"))
+        FileWrite(File, gSectionSep)
+        for Item in self.ItemList:
+            FileWrite(File, "*%sE  %-*s %s" % (Item[0], self.MaxLen, Item[1], Item[2]))
+            
+        FileWrite(File, gSectionStart)
+    
+    ##
+    # Generate Fixed Address report.
+    #
+    # This function generate the predicted fixed address report for a module
+    # specified by Guid.
+    #
+    # @param self            The object pointer
+    # @param File            The file object for report
+    # @param Guid            The module Guid value.
+    # @param NotifyList      The list of all notify function in a module
+    #
+    def _GenerateFixedAddressReport(self, File, Guid, NotifyList):
+        FixedAddressList = self.FixedMapDict.get(Guid)
+        if not FixedAddressList:
+            return
+        
+        FileWrite(File, gSubSectionStart)
+        FileWrite(File, "Fixed Address Prediction")
+        FileWrite(File, "*I  Image Loading Address")
+        FileWrite(File, "*E  Entry Point Address")
+        FileWrite(File, "*N  Notification Function Address")
+        FileWrite(File, "*F  Flash Address")
+        FileWrite(File, "*M  Memory Address")
+        FileWrite(File, "*S  SMM RAM Address")
+        FileWrite(File, "TOM Top of Memory")
+        
+        FileWrite(File, "Type Address           Name")
+        FileWrite(File, gSubSectionSep)
+        for Item in FixedAddressList:
+            Type = Item[0]
+            Value = Item[1]
+            Symbol = Item[2]
+            if Symbol == "*I":
+                Name = "(Image Base)"
+            elif Symbol == "*E":
+                Name = self._FfsEntryPoint.get(Guid, ["", "_ModuleEntryPoint"])[1]
+            elif Symbol in NotifyList:
+                Name = Symbol
+                Symbol = "*N"
+            else:
+                continue
+            
+            if "Flash" in Type:
+                Symbol += "F"
+            elif "Memory" in Type:
+                Symbol += "M"
+            else:
+                Symbol += "S"
+            
+            if Value[0] == "-":
+                Value = "TOM" + Value 
+
+            FileWrite(File, "%s  %-16s  %s" % (Symbol, Value, Name))
+
+    ##
+    # Generate report for the prediction part
+    #
+    # This function generate the predicted fixed address report for a module or
+    # predicted module execution order for a platform. 
+    # If the input Guid is None, then, it generates the predicted module execution order;
+    # otherwise it generated the module fixed loading address for the module specified by
+    # Guid.
+    #
+    # @param self            The object pointer
+    # @param File            The file object for report
+    # @param Guid            The module Guid value.
+    #
+    def GenerateReport(self, File, Guid):
+        self._ParseMapFile()
+        self._InvokeEotTool()
+        if Guid:
+            self._GenerateFixedAddressReport(File, Guid.upper(), [])
+        else:
+            self._GenerateExecutionOrderReport(File)
+
+##        
+# Reports FD region information
+#
+# This class reports the FD subsection in the build report file.
+# It collects region information of platform flash device. 
+# If the region is a firmware volume, it lists the set of modules
+# and its space information; otherwise, it only lists its region name,
+# base address and size in its sub-section header.
+# If there are nesting FVs, the nested FVs will list immediate after
+# this FD region subsection
+#
+class FdRegionReport(object):
+    ##
+    # Discover all the nested FV name list.
+    #
+    # This is an internal worker function to discover the all the nested FV information
+    # in the parent firmware volume. It uses deep first search algorithm recursively to
+    # find all the FV list name and append them to the list.
+    #
+    # @param self            The object pointer
+    # @param FvName          The name of current firmware file system
+    # @param Wa              Workspace context information
+    #
+    def _DiscoverNestedFvList(self, FvName, Wa):
+        for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
+            for Section in Ffs.SectionList:
+                try:
+                    for FvSection in Section.SectionList:
+                        if FvSection.FvName in self.FvList:
+                            continue
+                        self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName
+                        self.FvList.append(FvSection.FvName)
+                        self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0)
+                        self._DiscoverNestedFvList(FvSection.FvName, Wa)
+                except AttributeError:
+                    pass
+    
+    ##
+    # Constructor function for class FdRegionReport
+    #
+    # This constructor function generates FdRegionReport object for a specified FdRegion. 
+    # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
+    # volume list. This function also collects GUID map in order to dump module identification
+    # in the final report.
+    #
+    # @param self:           The object pointer
+    # @param FdRegion        The current FdRegion object
+    # @param Wa              Workspace context information
+    #
+    def __init__(self, FdRegion, Wa):
+        self.Type = FdRegion.RegionType
+        self.BaseAddress = FdRegion.Offset
+        self.Size = FdRegion.Size
+        self.FvList = []
+        self.FvInfo = {}
+        self._GuidsDb = {}
+        self._FvDir = Wa.FvDir
+
+        #
+        # If the input FdRegion is not a firmware volume,
+        # we are done. 
+        #
+        if self.Type != "FV":
+            return
+        
+        #
+        # Find all nested FVs in the FdRegion
+        #
+        for FvName in FdRegion.RegionDataList:
+            if FvName in self.FvList:
+                continue
+            self.FvList.append(FvName)
+            self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
+            self._DiscoverNestedFvList(FvName, Wa)
+
+        PlatformPcds = {}
+        for Pa in Wa.AutoGenObjectList:
+            PackageList = []
+            for ModuleKey in Pa.Platform.Modules:
+                #
+                # Collect PCD DEC default value.
+                #   
+                Module = Pa.Platform.Modules[ModuleKey]
+                for Package in Module.M.Module.Packages:
+                    if Package not in PackageList:
+                        PackageList.append(Package)
+                
+            for Package in PackageList:
+                for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
+                    DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
+                    PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
+        
+        #
+        # Collect PCDs defined in DSC common section
+        #
+        for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList:
+            for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
+                DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
+                PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
+        
+        #
+        # Add PEI and DXE a priori files GUIDs defined in PI specification.
+        #
+        self._GuidsDb["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
+        self._GuidsDb["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori" 
+        #
+        # Add ACPI table storage file
+        #
+        self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
+        
+        for Pa in Wa.AutoGenObjectList:
+            for ModuleKey in Pa.Platform.Modules:
+                M = Pa.Platform.Modules[ModuleKey].M
+                self._GuidsDb[M.Guid.upper()] = "%s (%s)" % (M.Module.BaseName, M.MetaFile.File)
+
+        #
+        # Collect the GUID map in the FV firmware volume
+        #
+        for FvName in self.FvList:
+            for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
+                try:
+                    #
+                    # collect GUID map for binary EFI file in FDF file.
+                    #
+                    Guid = Ffs.NameGuid.upper()
+                    Match = gPcdGuidPattern.match(Ffs.NameGuid)
+                    if Match:
+                        PcdTokenspace = Match.group(1)
+                        PcdToken = Match.group(2)
+                        if (PcdToken, PcdTokenspace) in PlatformPcds:
+                            GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)]
+                            Guid = GuidStructureByteArrayToGuidString(GuidValue).upper()
+                    for Section in Ffs.SectionList:
+                        try:
+                            ModuleSectFile = os.path.join(Wa.WorkspaceDir, Section.SectFileName)
+                            self._GuidsDb[Guid] = ModuleSectFile
+                        except AttributeError:
+                            pass
+                except AttributeError:
+                    pass
+        
+    
+    ##
+    # Internal worker function to generate report for the FD region
+    #
+    # This internal worker function to generate report for the FD region.
+    # It the type is firmware volume, it lists offset and module identification. 
+    #
+    # @param self            The object pointer
+    # @param File            The file object for report
+    # @param Title           The title for the FD subsection 
+    # @param BaseAddress     The base address for the FD region
+    # @param Size            The size of the FD region
+    # @param FvName          The FV name if the FD region is a firmware volume
+    #
+    def _GenerateReport(self, File, Title, Type, BaseAddress, Size=0, FvName=None):
+        FileWrite(File, gSubSectionStart)
+        FileWrite(File, Title)
+        FileWrite(File, "Type:               %s" % Type)
+        FileWrite(File, "Base Address:       0x%X" % BaseAddress)
+        
+        if self.Type == "FV":
+            FvTotalSize = 0
+            FvTakenSize = 0
+            FvFreeSize  = 0
+            FvReportFileName = os.path.join(self._FvDir, FvName + ".fv.txt")
+            try:
+                #
+                # Collect size info in the firmware volume.
+                #
+                FvReport = open(FvReportFileName).read()
+                Match = gFvTotalSizePattern.search(FvReport)
+                if Match:
+                    FvTotalSize = int(Match.group(1), 16)
+                Match = gFvTakenSizePattern.search(FvReport)
+                if Match:
+                    FvTakenSize = int(Match.group(1), 16)
+                FvFreeSize = FvTotalSize - FvTakenSize 
+                #
+                # Write size information to the report file.
+                #
+                FileWrite(File, "Size:               0x%X (%.0fK)" % (FvTotalSize, FvTotalSize / 1024.0))
+                FileWrite(File, "Fv Name:            %s (%.1f%% Full)" % (FvName, FvTakenSize * 100.0 / FvTotalSize))
+                FileWrite(File, "Occupied Size:      0x%X (%.0fK)" % (FvTakenSize, FvTakenSize / 1024.0))
+                FileWrite(File, "Free Size:          0x%X (%.0fK)" % (FvFreeSize, FvFreeSize / 1024.0))
+                FileWrite(File, "Offset     Module")
+                FileWrite(File, gSubSectionSep)
+                #
+                # Write module offset and module identification to the report file.
+                #
+                for Match in gOffsetGuidPattern.finditer(FvReport):
+                    Guid = Match.group(2).upper()
+                    Offset = int(Match.group(1), 16)
+                    FileWrite (File, "0x%07X %s" % (Offset, self._GuidsDb.get(Guid, Guid)))
+            except IOError:
+                EdkLogger.warn(None, "Fail to read report file", FvReportFileName)
+        else:
+            FileWrite(File, "Size:               0x%X (%.0fK)" % (Size, Size / 1024.0))
+        FileWrite(File, gSubSectionEnd)
+
+    ##
+    # Generate report for the FD region
+    #
+    # This function generates report for the FD region. 
+    #
+    # @param self            The object pointer
+    # @param File            The file object for report
+    #
+    def GenerateReport(self, File):
+        if (len(self.FvList) > 0):
+            for FvItem in self.FvList:
+                Info = self.FvInfo[FvItem]
+                self._GenerateReport(File, Info[0], "FV", Info[1], Info[2], FvItem)
+        else:
+            self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size)    
+            
+##
+# Reports FD information
+#
+# This class reports the FD section in the build report file.
+# It collects flash device information for a platform. 
+#
+class FdReport(object):
+    ##
+    # Constructor function for class FdReport
+    #
+    # This constructor function generates FdReport object for a specified
+    # firmware device. 
+    #
+    # @param self            The object pointer
+    # @param Fd              The current Firmware device object
+    # @param Wa              Workspace context information
+    #
+    def __init__(self, Fd, Wa):
+        self.FdName = Fd.FdUiName
+        self.BaseAddress = Fd.BaseAddress
+        self.Size = Fd.Size
+        self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList]
+
+    ##
+    # Generate report for the firmware device.
+    #
+    # This function generates report for the firmware device. 
+    #
+    # @param self            The object pointer
+    # @param File            The file object for report
+    #
+    def GenerateReport(self, File):
+        FileWrite(File, gSectionStart)
+        FileWrite(File, "Firmware Device (FD)")
+        FileWrite(File, "FD Name:            %s" % self.FdName)
+        FileWrite(File, "Base Address:       0x%s" % self.BaseAddress)
+        FileWrite(File, "Size:               0x%X (%.0fK)" % (self.Size, self.Size / 1024.0))
+        if len(self.FdRegionList) > 0:
+            FileWrite(File, gSectionSep)
+            for FdRegionItem in self.FdRegionList:
+                FdRegionItem.GenerateReport(File)
+        
+        FileWrite(File, gSectionEnd)
+
+
+    
 ##
 # Reports platform information
 #
@@ -627,6 +1270,15 @@ class PlatformReport(object):
         if "PCD" in ReportType:
             self.PcdReport = PcdReport(Wa)
 
+        self.FdReportList = []
+        if "FLASH" in ReportType and Wa.FdfProfile:
+            for Fd in Wa.FdfProfile.FdDict:
+                self.FdReportList.append(FdReport(Wa.FdfProfile.FdDict[Fd], Wa))
+        
+        self.PredictionReport = None        
+        if "PREDICTION" in ReportType:
+            self.PredictionReport = PredictionReport(Wa)
+        
         self.ModuleReportList = []
         for Pa in Wa.AutoGenObjectList:
             for ModuleKey in Pa.Platform.Modules:
@@ -638,6 +1290,8 @@ class PlatformReport(object):
                     DscOverridePcds = {}
                 self.ModuleReportList.append(ModuleReport(Pa.Platform.Modules[ModuleKey].M, DscOverridePcds, ReportType))
  
+
+        
     ##
     # Generate report for the whole platform.
     #
@@ -665,10 +1319,15 @@ class PlatformReport(object):
         if "PCD" in ReportType:
             self.PcdReport.GenerateReport(File, None, {})
             
-       
+        if "FLASH" in ReportType:
+            for FdReportListItem in self.FdReportList:
+                FdReportListItem.GenerateReport(File)
+        
         for ModuleReportItem in self.ModuleReportList:
-            ModuleReportItem.GenerateReport(File, self.PcdReport, ReportType)
+            ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, ReportType)
         
+        if "PREDICTION" in ReportType:
+            self.PredictionReport.GenerateReport(File, None)
 
 ## BuildReport class
 #
@@ -691,6 +1350,7 @@ class BuildReport(object):
         self.ReportFile = ReportFile
         if ReportFile:
             self.ReportList = []
+            self.ReportType = []
             if ReportType == None or "ALL" in ReportType:
                 self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "PREDICTION"]
             else: