Add flash related features for build report generation tools
authorqhuang8 <qhuang8@c2973edb-eda0-4c78-bc6a-9341b269661f>
Tue, 26 Jan 2010 06:14:58 +0000 (06:14 +0000)
committerqhuang8 <qhuang8@c2973edb-eda0-4c78-bc6a-9341b269661f>
Tue, 26 Jan 2010 06:14:58 +0000 (06:14 +0000)
git-svn-id: https://edk2-buildtools.svn.sourceforge.net/svnroot/edk2-buildtools/trunk/BaseTools@1815 c2973edb-eda0-4c78-bc6a-9341b269661f

Source/C/GenFv/GenFvInternalLib.c
Source/C/GenFw/GenFw.c
Source/Python/build/BuildReport.py

index 49bb5d3..f3cbdd5 100644 (file)
@@ -1,6 +1,6 @@
 /** @file\r
 \r
-Copyright (c) 2004 - 2009, Intel Corporation                                                         \r
+Copyright (c) 2004 - 2010, Intel Corporation                                                         \r
 All rights reserved. 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
@@ -901,7 +901,8 @@ AddFile (
   IN FV_INFO                  *FvInfo,\r
   IN UINTN                    Index,\r
   IN OUT EFI_FFS_FILE_HEADER  **VtfFileImage,\r
-  IN FILE                     *FvMapFile  \r
+  IN FILE                     *FvMapFile,\r
+  IN FILE                     *FvReportFile\r
   )\r
 /*++\r
 \r
@@ -919,6 +920,7 @@ Arguments:
   VtfFileImage  A pointer to the VTF file within the FvImage.  If this is equal\r
                 to the end of the FvImage then no VTF previously found.\r
   FvMapFile     Pointer to FvMap File\r
+  FvReportFile  Pointer to FvReport File\r
 \r
 Returns:\r
 \r
@@ -936,6 +938,7 @@ Returns:
   UINT32                CurrentFileAlignment;\r
   EFI_STATUS            Status;\r
   UINTN                 Index1;\r
+  UINT8                 FileGuidString[PRINTED_GUID_BUFFER_SIZE];\r
   \r
   Index1 = 0;\r
   //\r
@@ -1109,6 +1112,8 @@ Returns:
     // Copy the file\r
     //\r
     memcpy (FvImage->CurrentFilePointer, FileBuffer, FileSize);\r
+    PrintGuidToBuffer ((EFI_GUID *) FileBuffer, FileGuidString, sizeof (FileGuidString), TRUE); \r
+    fprintf (FvReportFile, "0x%08x %s\n", (unsigned) (FvImage->CurrentFilePointer - FvImage->FileImage), FileGuidString);\r
     FvImage->CurrentFilePointer += FileSize;\r
   } else {\r
     Error (NULL, 0, 4002, "Resource", "FV space is full, cannot add file %s.", FvInfo->FvFiles[Index]);\r
@@ -1970,10 +1975,13 @@ Returns:
   EFI_FIRMWARE_VOLUME_EXT_HEADER  *FvExtHeader;\r
   FILE                            *FvExtHeaderFile;\r
   UINTN                           FileSize;\r
+  CHAR8                           FvReportName[_MAX_PATH];\r
+  FILE                            *FvReportFile;\r
 \r
   FvBufferHeader = NULL;\r
   FvFile         = NULL;\r
   FvMapFile      = NULL;\r
+  FvReportFile   = NULL;\r
 \r
   if (InfFileImage != NULL) {\r
     //\r
@@ -2112,6 +2120,12 @@ Returns:
   }\r
   VerboseMsg ("FV Map file name is %s", FvMapName);\r
 \r
+  //\r
+  // FvReport file to log the FV information in one Fvimage\r
+  //\r
+  strcpy (FvReportName, FvFileName);\r
+  strcat (FvReportName, ".txt");\r
+\r
   //\r
   // Calculate the FV size and Update Fv Size based on the actual FFS files.\r
   // And Update mFvDataInfo data.\r
@@ -2227,6 +2241,14 @@ Returns:
     return EFI_ABORTED;\r
   }\r
   \r
+  //\r
+  // Open FvReport file\r
+  //\r
+  FvReportFile = fopen(FvReportName, "w");\r
+  if (FvReportFile == NULL) {\r
+    Error (NULL, 0, 0001, "Error opening file", FvReportFile);\r
+    return EFI_ABORTED;\r
+  }\r
   //\r
   // record FV size information into FvMap file.\r
   //\r
@@ -2243,6 +2265,12 @@ Returns:
     fprintf (FvMapFile, " = 0x%x\n\n", (unsigned) (mFvTotalSize - mFvTakenSize));\r
   }\r
 \r
+  //\r
+  // record FV size information to FvReportFile.\r
+  //\r
+  fprintf (FvReportFile, "%s = 0x%x\n", EFI_FV_TOTAL_SIZE_STRING, (unsigned) mFvTotalSize);\r
+  fprintf (FvReportFile, "%s = 0x%x\n", EFI_FV_TAKEN_SIZE_STRING, (unsigned) mFvTakenSize);\r
+\r
   //\r
   // Add PI FV extension header\r
   //\r
@@ -2266,7 +2294,7 @@ Returns:
     //\r
     // Add the file\r
     //\r
-    Status = AddFile (&FvImageMemoryFile, &mFvDataInfo, Index, &VtfFileImage, FvMapFile);\r
+    Status = AddFile (&FvImageMemoryFile, &mFvDataInfo, Index, &VtfFileImage, FvMapFile, FvReportFile);\r
 \r
     //\r
     // Exit if error detected while adding the file\r
@@ -2368,6 +2396,9 @@ Finish:
     fclose (FvMapFile);\r
   }\r
 \r
+  if (FvReportFile != NULL) {\r
+    fclose (FvReportFile);\r
+  }\r
   return Status;\r
 }\r
 \r
index aabd143..a7091bd 100644 (file)
@@ -1,6 +1,6 @@
 /** @file\r
 \r
-Copyright (c) 2004 - 2009, Intel Corporation\r
+Copyright (c) 2004 - 2010, Intel Corporation\r
 All rights reserved. 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
@@ -116,6 +116,12 @@ static const char *gHiiPackageRCFileHeader[] = {
 \r
 STATIC CHAR8 *mInImageName;\r
 \r
+//\r
+// Module image information\r
+//\r
+STATIC UINT32 mImageTimeStamp = 0;\r
+STATIC UINT32 mImageSize = 0;\r
+\r
 STATIC\r
 EFI_STATUS\r
 ZeroDebugData (\r
@@ -860,6 +866,7 @@ ScanSections(
 \r
   NtHdr->Pe32.FileHeader.NumberOfSections = CoffNbrSections;\r
   NtHdr->Pe32.FileHeader.TimeDateStamp = (UINT32) time(NULL);\r
+  mImageTimeStamp = NtHdr->Pe32.FileHeader.TimeDateStamp;\r
   NtHdr->Pe32.FileHeader.PointerToSymbolTable = 0;\r
   NtHdr->Pe32.FileHeader.NumberOfSymbols = 0;\r
   NtHdr->Pe32.FileHeader.SizeOfOptionalHeader = sizeof(NtHdr->Pe32.OptionalHeader);\r
@@ -1712,6 +1719,9 @@ Returns:
   EFI_HII_PACKAGE_HEADER           EndPackage;\r
   UINT32                           HiiSectionHeaderSize;\r
   UINT8                            *HiiSectionHeader;\r
+  FILE                             *ReportFile;\r
+  CHAR8                            *ReportFileName;\r
+  UINTN                            FileLen;\r
 \r
   SetUtilityName (UTILITY_NAME);\r
 \r
@@ -3128,6 +3138,7 @@ WriteFile:
     fwrite (FileBuffer, 1, FileLength, fpInOut);\r
   }\r
   VerboseMsg ("the size of output file is %u bytes", (unsigned) FileLength);\r
+  mImageSize = FileLength;\r
 \r
 Finish:\r
   if (fpInOut != NULL) {\r
@@ -3168,6 +3179,24 @@ Finish:
     }\r
   }\r
 \r
+  //\r
+  // Write module size and time stamp to report file.\r
+  //\r
+  FileLen = strlen (OutImageName);\r
+  if (FileLen >= 4 && strcmp (OutImageName + (FileLen - 4), ".efi") == 0) {\r
+    ReportFileName = (CHAR8 *) malloc (FileLen + 1);\r
+    if (ReportFileName != NULL) {\r
+      strcpy (ReportFileName, OutImageName);\r
+      strcpy (ReportFileName + (FileLen - 4), ".txt"); \r
+      ReportFile = fopen (ReportFileName, "w+");\r
+      if (ReportFile != NULL) {\r
+        fprintf (ReportFile, "MODULE_SIZE = %u\n", (unsigned) mImageSize);\r
+        fprintf (ReportFile, "TIME_STAMP = %u\n", (unsigned) mImageTimeStamp);\r
+        fclose(ReportFile);\r
+      }\r
+      free (ReportFileName);\r
+    }\r
+  }\r
   VerboseMsg ("%s tool done with return code is 0x%x.", UTILITY_NAME, GetUtilityStatus ());\r
 \r
   return GetUtilityStatus ();\r
@@ -3302,7 +3331,7 @@ Returns:
   //Zero Debug Data and TimeStamp\r
   //\r
   FileHdr->TimeDateStamp = 0;\r
-\r
+  mImageTimeStamp = 0;\r
   if (ExportDirectoryEntryFileOffset != 0) {\r
     NewTimeStamp  = (UINT32 *) (FileBuffer + ExportDirectoryEntryFileOffset + sizeof (UINT32));\r
     *NewTimeStamp = 0;\r
@@ -3316,6 +3345,7 @@ Returns:
   if (DebugDirectoryEntryFileOffset != 0) {\r
     DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) (FileBuffer + DebugDirectoryEntryFileOffset);\r
     DebugEntry->TimeDateStamp = 0;\r
+    mImageTimeStamp = 0;\r
     if (ZeroDebugFlag) {\r
       memset (FileBuffer + DebugEntry->FileOffset, 0, DebugEntry->SizeOfData);\r
       memset (DebugEntry, 0, sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY));\r
@@ -3542,7 +3572,7 @@ Returns:
   // Set new stamp\r
   //\r
   FileHdr->TimeDateStamp = (UINT32) newtime;\r
-\r
+  mImageTimeStamp = (UINT32) newtime;\r
   if (ExportDirectoryEntryRva != 0) {\r
     NewTimeStamp  = (UINT32 *) (FileBuffer + ExportDirectoryEntryFileOffset + sizeof (UINT32));\r
     *NewTimeStamp = (UINT32) newtime;\r
index 71b9e02..9e4507a 100755 (executable)
@@ -20,7 +20,10 @@ import os
 import re
 import platform
 import textwrap
+from datetime import datetime
 from Common import EdkLogger
+from Common.Misc import GuidStructureByteArrayToGuidString
+from Common.InfClassObject import gComponentType2ModuleType
 from Common.BuildToolError import FILE_OPEN_FAILURE
 from Common.BuildToolError import FILE_WRITE_FAILURE
 
@@ -32,6 +35,10 @@ 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+)\)")
 
@@ -61,6 +68,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.
 # 
@@ -286,7 +308,8 @@ class BuildFlagsReport(object):
             FileWrite(File, "%s = %s" % (Tool, self.BuildFlags[Tool]), True)
         
         FileWrite(File, gSubSectionEnd)
-                       
+
+
 ##
 # Reports individual module information
 #
@@ -311,14 +334,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 +374,7 @@ class ModuleReport(object):
         
         if "BUILD_FLAGS" in ReportType:
             self.BuildFlagsReport = BuildFlagsReport(M)
+        
     
     ##
     # Generate report for module information
@@ -361,12 +390,26 @@ class ModuleReport(object):
     def GenerateReport(self, File, GlobalPcdReport, 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:
@@ -597,6 +640,285 @@ class PcdReport(object):
             FileWrite(File, gSubSectionEnd)     
 
 
+##
+# 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"
+        #
+        # 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, module mapping from INF file
+                    #
+                    InfFileName = Ffs.InfFileName
+                    try:
+                        FileGuid = ""
+                        ModuleName = ""
+                        InfPath = os.path.join(Wa.WorkspaceDir, InfFileName)
+                        for Line in open(InfPath):
+                            ItemList = Line.split("#")[0].split("=")
+                            if len(ItemList) == 2:
+                                Key   = ItemList[0].strip().upper()
+                                Value = ItemList[1].strip()
+                                if Key == "FILE_GUID":
+                                    FileGuid = Value.upper()
+                                if Key == "BASE_NAME":
+                                    ModuleName = Value
+                        if FileGuid:
+                            self._GuidsDb[FileGuid] = "%s (%s)" % (ModuleName, InfPath)
+                    except IOError:
+                        EdkLogger.warn(None, "Cannot open file to read", InfPath)
+                except AttributeError:
+                    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 +949,11 @@ 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.ModuleReportList = []
         for Pa in Wa.AutoGenObjectList:
             for ModuleKey in Pa.Platform.Modules:
@@ -665,7 +992,10 @@ 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)