778 lines
26 KiB
Python
778 lines
26 KiB
Python
## @ GenReport.py
|
|
#
|
|
# Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
|
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
#
|
|
##
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
import uuid
|
|
from ctypes import *
|
|
from functools import reduce
|
|
|
|
sys.dont_write_bytecode = True
|
|
from BuildUtility import STITCH_OPS
|
|
|
|
def Bytes2Val(bytes):
|
|
return reduce(lambda x, y: (x << 8) | y, bytes[::-1])
|
|
|
|
|
|
def Val2Bytes(value, blen):
|
|
return [(value >> (i * 8) & 0xff) for i in range(blen)]
|
|
|
|
|
|
def AlignPtrUp(offset, alignment=8):
|
|
return (offset + alignment - 1) & ~(alignment - 1)
|
|
|
|
|
|
def AlignPtrDown(offset, alignment=8):
|
|
return offset & ~(alignment - 1)
|
|
|
|
def ExecAssignment (var, val):
|
|
namespace = {}
|
|
exec ('%s = %s' % (var, val), namespace)
|
|
return namespace[var]
|
|
|
|
class c_uint24(Structure):
|
|
_pack_ = 1
|
|
_fields_ = [('Data', (c_uint8 * 3))]
|
|
|
|
def __init__(self, val=0):
|
|
self.set_value(val)
|
|
|
|
def __str__(self, indent=0):
|
|
return '0x%.6x' % self.value
|
|
|
|
def __int__(self):
|
|
return self.get_value()
|
|
|
|
def set_value(self, val):
|
|
self.Data[0:3] = Val2Bytes(val, 3)
|
|
|
|
def get_value(self):
|
|
return Bytes2Val(self.Data[0:3])
|
|
|
|
value = property(get_value, set_value)
|
|
|
|
class EFI_FIRMWARE_VOLUME_HEADER(Structure):
|
|
_fields_ = [
|
|
('ZeroVector', ARRAY(c_uint8, 16)),
|
|
('FileSystemGuid', ARRAY(c_uint8, 16)),
|
|
('FvLength', c_uint64),
|
|
('Signature', ARRAY(c_char, 4)),
|
|
('Attributes', c_uint32),
|
|
('HeaderLength', c_uint16),
|
|
('Checksum', c_uint16),
|
|
('ExtHeaderOffset', c_uint16),
|
|
('Reserved', c_uint8),
|
|
('Revision', c_uint8)
|
|
]
|
|
|
|
|
|
class EFI_FIRMWARE_VOLUME_EXT_HEADER(Structure):
|
|
_fields_ = [
|
|
('FvName', ARRAY(c_uint8, 16)),
|
|
('ExtHeaderSize', c_uint32)
|
|
]
|
|
|
|
|
|
class EFI_FFS_INTEGRITY_CHECK(Structure):
|
|
_fields_ = [
|
|
('Header', c_uint8),
|
|
('File', c_uint8)
|
|
]
|
|
|
|
|
|
class EFI_FFS_FILE_HEADER(Structure):
|
|
_fields_ = [
|
|
('Name', ARRAY(c_uint8, 16)),
|
|
('IntegrityCheck', EFI_FFS_INTEGRITY_CHECK),
|
|
('Type', c_uint8),
|
|
('Attributes', c_uint8),
|
|
('Size', c_uint24),
|
|
('State', c_uint8)
|
|
]
|
|
|
|
|
|
class FSP_INFORMATION_HEADER(Structure):
|
|
_fields_ = [
|
|
('Signature', ARRAY(c_char, 4)),
|
|
('HeaderLength', c_uint32),
|
|
('Reserved1', c_uint16),
|
|
('SpecVersion', c_uint8),
|
|
('HeaderRevision', c_uint8),
|
|
('ImageRevision', c_uint32),
|
|
('ImageId', ARRAY(c_char, 8)),
|
|
('ImageSize', c_uint32),
|
|
('ImageBase', c_uint32),
|
|
('ImageAttribute', c_uint16),
|
|
('ComponentAttribute', c_uint16),
|
|
('CfgRegionOffset', c_uint32),
|
|
('CfgRegionSize', c_uint32),
|
|
('Reserved2', c_uint32),
|
|
('TempRamInitEntryOffset', c_uint32),
|
|
('Reserved3', c_uint32),
|
|
('NotifyPhaseEntryOffset', c_uint32),
|
|
('FspMemoryInitEntryOffset', c_uint32),
|
|
('TempRamExitEntryOffset', c_uint32),
|
|
('FspSiliconInitEntryOffset', c_uint32)
|
|
]
|
|
|
|
|
|
class IMG_INFO:
|
|
def __init__(self, Type, Name, Offset=0, Length=0, Base=0):
|
|
if Type == 'IMG':
|
|
self.Level = 0
|
|
elif Type == 'FD':
|
|
self.Level = 1
|
|
elif Type == 'FV' or Type == 'FSP':
|
|
self.Level = 2
|
|
elif Type == 'FFS':
|
|
self.Level = 3
|
|
else:
|
|
raise Exception('Invalid type "%s" !' % Type)
|
|
self.Name = Name
|
|
self.Type = Type
|
|
self.Offset = Offset
|
|
self.Length = Length
|
|
self.Base = Base
|
|
self.ChildList = []
|
|
|
|
def __str__(self):
|
|
Lines = []
|
|
Indent = ' ' * self.Level
|
|
Lines.append(Indent + 'Type : %s' % self.Type)
|
|
Lines.append(Indent + 'Name : %s' % self.Name)
|
|
Lines.append(Indent + 'Offset : 0x%08X' % self.Offset)
|
|
Lines.append(Indent + 'Length : 0x%08X' % self.Length)
|
|
Lines.append(Indent + 'Base : 0x%08X' % self.Base)
|
|
ChildLine = 'ChildList:'
|
|
if not len(self.ChildList):
|
|
ChildLine = ChildLine + ' []'
|
|
Lines.append(Indent + ChildLine)
|
|
else:
|
|
Lines.append(Indent + ChildLine)
|
|
for Child in self.ChildList:
|
|
Lines.append(str(Child))
|
|
Lines.append('\n')
|
|
return '\n'.join(Lines)
|
|
|
|
|
|
class REPORTER:
|
|
Cols = [4, 13, 15, 30]
|
|
|
|
def __init__(self, ImgName, RptType='FD'):
|
|
self.ImgName = ImgName
|
|
self.RptType = RptType
|
|
if RptType == 'FD':
|
|
self.Header = ['', 'FD', 'FV', 'FFS']
|
|
else:
|
|
self.Header = ['', 'Name', 'File', 'Details']
|
|
self.Layout = [[], [], [], []]
|
|
self.WordWrap = ['STAGE1', 'STAGE1A', 'STAGE1B', 'STAGE2']
|
|
self.DictDscDefines = {}
|
|
self.DictGuidNameXref = {
|
|
'1BA0062E-C779-4582-8566-336AE8F78F09': 'ResetVector',
|
|
'E08CA6D5-8D02-43AE-ABB1-952CC787C933': 'VbtBin',
|
|
'26FDAA3D-B7ED-4714-8509-EECF1593800D': 'AcmBin',
|
|
'5E2D3BE9-AD72-4D1D-AAD5-6B08AF921590': 'Logo',
|
|
'3473A022-C3C2-4964-B309-22B3DFB0B6CA': 'VerInfo',
|
|
'18EDB1DF-1DBE-4EC5-8E26-C44808B546E1': 'HashStore',
|
|
'EFAC3859-B680-4232-A159-F886F2AE0B83': 'PcdDatabase',
|
|
'CD17FF5E-7731-4D16-8441-FC7A113C392F': 'FitTable',
|
|
'3CEA8EF3-95FC-476F-ABA5-7EC5DFA1D77B': 'FlashMap',
|
|
'FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF': 'BLANK'
|
|
}
|
|
self.ColsWidth = [4, 13, 15, 30]
|
|
|
|
def GetFspType(self, Attribute):
|
|
return "XTMSXXXXOXXXXXXX" [(Attribute >> 12) & 0x0F]
|
|
|
|
def GetFfsSize(self, FvFile, FfsOffset):
|
|
FdIn = open(FvFile, "rb")
|
|
FdIn.seek(FfsOffset)
|
|
Buffer = bytearray(FdIn.read(sizeof(EFI_FFS_FILE_HEADER)))
|
|
FdIn.close()
|
|
FfsHdr = EFI_FFS_FILE_HEADER.from_buffer(Buffer)
|
|
return int(FfsHdr.Size)
|
|
|
|
def GetFvFfsList(self, FvData):
|
|
FfsList = []
|
|
FvHdr = EFI_FIRMWARE_VOLUME_HEADER.from_buffer(FvData, 0)
|
|
if FvHdr.ExtHeaderOffset > 0:
|
|
FvExtHdr = EFI_FIRMWARE_VOLUME_EXT_HEADER.from_buffer(
|
|
Buffer, FvHdr.ExtHeaderOffset)
|
|
Offset = FvHdr.ExtHeaderOffset + FvExtHdr.ExtHeaderSize
|
|
else:
|
|
Offset = FvHdr.HeaderLength
|
|
|
|
while Offset < FvHdr.FvLength:
|
|
Offset = AlignPtrUp(Offset)
|
|
FfsHdr = EFI_FFS_FILE_HEADER.from_buffer(FvData, Offset)
|
|
FfsName = str(uuid.UUID(bytes_le=bytes(bytearray(FfsHdr.Name)))).upper()
|
|
Ffs = IMG_INFO('FFS', FfsName, Offset, int(FfsHdr.Size))
|
|
if bytearray(FfsHdr.Name) == b'\xff' * 16:
|
|
if (int(FfsHdr.Size) == 0xFFFFFF):
|
|
Ffs.Length = FvHdr.FvLength - Offset
|
|
Offset += Ffs.Length
|
|
FfsList.append(Ffs)
|
|
|
|
return FfsList
|
|
|
|
def ParseFdFile(self, FdFile):
|
|
FdSize = os.path.getsize(FdFile)
|
|
if FdSize < sizeof(EFI_FIRMWARE_VOLUME_HEADER):
|
|
return None
|
|
Fd = IMG_INFO('FD', os.path.basename(FdFile), 0, FdSize)
|
|
FvOffset = 0
|
|
FdIn = open(FdFile, "rb")
|
|
FdData = bytearray(FdIn.read())
|
|
FdIn.close()
|
|
while FvOffset < FdSize:
|
|
FvHdr = EFI_FIRMWARE_VOLUME_HEADER.from_buffer(FdData, FvOffset)
|
|
if b'_FVH' != FvHdr.Signature:
|
|
raise Exception("ERROR: Invalid FV header in FD '%s' !" %
|
|
FdFile)
|
|
FspHdr = FSP_INFORMATION_HEADER.from_buffer(FdData,
|
|
FvOffset + 0x94)
|
|
OldOffset = FvOffset
|
|
if b'FSPH' == FspHdr.Signature:
|
|
FvOffset += FspHdr.ImageSize
|
|
Fv = IMG_INFO(
|
|
'FSP', 'FSP-' + self.GetFspType(FspHdr.ComponentAttribute),
|
|
0, FspHdr.ImageSize, FspHdr.ImageBase)
|
|
Ffs = IMG_INFO('FFS', Fv.Name, 0, Fv.Length)
|
|
Fv.ChildList = [Ffs]
|
|
else:
|
|
FvOffset += FvHdr.FvLength
|
|
Fv = IMG_INFO('FV', 'UNKNOWN', 0, FvHdr.FvLength)
|
|
Fv.ChildList = self.GetFvFfsList(FdData[OldOffset:])
|
|
FirstFfs = Fv.ChildList[0]
|
|
FirstFfsName = self.GuidToModuleName(FirstFfs.Name)
|
|
if FirstFfsName.upper() == 'ACMBIN':
|
|
# For ACM FV, it contains ACM, BPM, KM, etc
|
|
# so let ACM occupy the full FV and ignore all other FFS
|
|
while len(Fv.ChildList) > 1:
|
|
Fv.ChildList.pop()
|
|
Fv.ChildList[0].Offset = 0
|
|
Fv.ChildList[0].Length = Fv.Length
|
|
|
|
if Fd.Name.startswith('STAGE2.'):
|
|
if ('STAGE2_LOAD_HIGH' in self.DictDscDefines) and \
|
|
(int(self.DictDscDefines['STAGE2_LOAD_HIGH'], 16) == 1):
|
|
# Mark FV/FSP will be loaded dynamically
|
|
Fv.Base = 0xFFFFFFFF
|
|
Fv.Offset = OldOffset
|
|
Fd.ChildList.append(Fv)
|
|
if FvOffset != FdSize:
|
|
raise Exception('Invalid FD format !')
|
|
return Fd
|
|
|
|
def ParseFvTxtFile(self, FvTxtFile):
|
|
FvName = os.path.basename(FvTxtFile)[0:-7].upper()
|
|
Fv = IMG_INFO('FV', FvName + '.FV')
|
|
|
|
FdIn = open(FvTxtFile, "r")
|
|
Lines = FdIn.readlines()
|
|
FdIn.close()
|
|
for Line in Lines:
|
|
Match = re.match("EFI_FV_TOTAL_SIZE\s+=\s+(0x[a-fA-F0-9]+)", Line)
|
|
if Match is not None:
|
|
Fv.Length = int(Match.group(1), 16)
|
|
continue
|
|
Match = re.match("(0x[a-fA-F0-9]+)\s([0-9a-fA-F\-]+)", Line)
|
|
if Match is not None:
|
|
Ffs = IMG_INFO('FFS', Match.group(2).upper())
|
|
Ffs.Offset = int(Match.group(1), 16)
|
|
Ffs.Length = self.GetFfsSize(FvTxtFile[:-4], Ffs.Offset)
|
|
Fv.ChildList.append(Ffs)
|
|
|
|
FdIn = open(FvTxtFile[0:-7] + '.inf', "r")
|
|
Lines = FdIn.readlines()
|
|
FdIn.close()
|
|
for Line in Lines:
|
|
Match = re.match("EFI_BASE_ADDRESS\s+=\s+(0x[a-fA-F0-9]+)", Line)
|
|
if Match is not None:
|
|
# Update base if it is not dynamic loading
|
|
if Fv.Base != 0xFFFFFFFF:
|
|
Fv.Base = int(Match.group(1), 16)
|
|
break
|
|
|
|
return Fv
|
|
|
|
def ParseGuidXrefFile(self, XrefFile):
|
|
FdIn = open(XrefFile, "r")
|
|
Lines = FdIn.readlines()
|
|
FdIn.close()
|
|
for Line in Lines:
|
|
Match = re.match("([0-9a-fA-F\-]+)\s(.+)", Line)
|
|
if Match:
|
|
Guid = Match.group(1).upper()
|
|
if Guid not in self.DictGuidNameXref:
|
|
Path = Match.group(2).strip()
|
|
Name = os.path.basename(Path)
|
|
self.DictGuidNameXref[Match.group(1).upper()] = Name
|
|
|
|
def ParseDscParameter(self, DefineDscFile):
|
|
FdIn = open(DefineDscFile, "r")
|
|
Lines = FdIn.readlines()
|
|
FdIn.close()
|
|
|
|
IsDefSection = False
|
|
for Line in Lines:
|
|
Line = Line.strip()
|
|
if Line.startswith('#'):
|
|
continue
|
|
if Line.startswith('['):
|
|
if Line.upper().startswith('[DEFINES]'):
|
|
IsDefSection = True
|
|
else:
|
|
IsDefSection = False
|
|
if IsDefSection:
|
|
Match = re.match("^DEFINE\s+([_\w]+)\s*=\s*([_\w]+)", Line)
|
|
if Match is not None:
|
|
self.DictDscDefines[Match.group(1).upper()] = Match.group(
|
|
2)
|
|
|
|
def GuidToModuleName(self, Guid):
|
|
if Guid in self.DictGuidNameXref:
|
|
return self.DictGuidNameXref[Guid]
|
|
else:
|
|
if len(Guid) < 0x24:
|
|
return Guid
|
|
else:
|
|
return '????'
|
|
|
|
def Report(self, FdList, CellHeight=3):
|
|
Cols = self.ColsWidth
|
|
LineHeight = CellHeight
|
|
|
|
def Center(Idx, ValueIn, Pattern):
|
|
Value = ValueIn.strip()
|
|
if Value == '':
|
|
Value = Pattern[2 * Idx + 1] * (REPORTER.Cols[Idx] - 1)
|
|
Space = REPORTER.Cols[Idx] - 1
|
|
Blank = Space - len(Value)
|
|
if Blank < 0:
|
|
Blank = 0
|
|
Value = Value[:Space]
|
|
Lead = Blank // 2
|
|
Tail = Blank - Lead
|
|
return ' ' * Lead + Value + ' ' * Tail + Pattern[2 * Idx + 2]
|
|
|
|
def OutSplitter(Pattern='+-+-+-+-+', Value=['', '', '', '']):
|
|
Lines = []
|
|
Lines.append(Pattern[0])
|
|
for i, j in enumerate(REPORTER.Cols):
|
|
Lines.append(Center(i, Value[i], Pattern))
|
|
return ' ' + ''.join(Lines)
|
|
|
|
def FillTable(Table, Base, TotalRow, Col, InputLine):
|
|
Lines = InputLine.split('!')
|
|
Count = TotalRow - len(Lines)
|
|
if Count < 0:
|
|
Start = 0
|
|
else:
|
|
Start = Base + (Count >> 1)
|
|
for Line in Lines:
|
|
if Start + 1 >= len(Table[Col]):
|
|
break
|
|
Table[Col][Start] = Line
|
|
Start += 1
|
|
|
|
def GetPattern(Layout, LineIdx):
|
|
Pattern = [ord(i) for i in '| + + + |']
|
|
for Col in range(len(Layout)):
|
|
Cnt = 0
|
|
Idx = Col * 2 + 1
|
|
for Val in Layout[Col]:
|
|
Cnt += Val
|
|
if LineIdx == Cnt - 1:
|
|
Pattern[Idx] = ord('-')
|
|
break
|
|
if LineIdx == sum(Layout[0]) - 1:
|
|
# Last line
|
|
Pattern[0] = ord('+')
|
|
Pattern[-1] = ord('+')
|
|
|
|
for Idx, Char in enumerate(Pattern):
|
|
if Char == ord('+') and Idx > 0 and Idx < len(Pattern) - 1:
|
|
# Check orphan in a row
|
|
if Pattern[Idx - 1] == ord(' ') and Pattern[Idx + 1] == ord(' '):
|
|
Pattern[Idx] = ord('|')
|
|
Pattern = ''.join(chr(i) for i in Pattern)
|
|
return Pattern
|
|
|
|
def Normalize(Com):
|
|
if Com.Type == 'FFS':
|
|
Name = self.GuidToModuleName(Com.Name)
|
|
else:
|
|
Name = Com.Name
|
|
if Com.Type in ['FV', 'FSP']:
|
|
if Com.Base == 0xFFFFFFFF:
|
|
Display = 'DYNAMIC'
|
|
else:
|
|
Display = '0x%X' % Com.Base
|
|
Name = '%s!(0x%X)!%s' % (Name, Com.Length, Display)
|
|
else:
|
|
Name = '%s!(0x%X)' % (Name, Com.Length)
|
|
Lines = Name.split('!')
|
|
Output = []
|
|
for Line in Lines:
|
|
Part = Line.split('.')[0]
|
|
Part = Part.replace(',', '.')
|
|
if len(Part) + 1 >= REPORTER.Cols[Com.Level]:
|
|
Idx = 0
|
|
for Word in self.WordWrap:
|
|
if Part.startswith(Word):
|
|
Idx = len(Word)
|
|
break
|
|
if Idx == 0:
|
|
Idx = len(Part) // 2
|
|
Part = '%s!%s' % (Part[:Idx], Part[Idx:])
|
|
Output.append(Part)
|
|
return '!'.join(Output)
|
|
|
|
def CalcRows(Image):
|
|
Layout = [[], [], [], []]
|
|
Idx1 = 0
|
|
for Img, FdList in Image:
|
|
Idx2 = 0
|
|
for Fd, FvList in FdList:
|
|
for Fv, FfsList in FvList:
|
|
FfsNum = 0
|
|
for Ffs in FfsList:
|
|
if Ffs.startswith('FSP'):
|
|
Adjust = 4
|
|
else:
|
|
Adjust = LineHeight
|
|
FfsNum += Adjust
|
|
Layout[3].append(Adjust)
|
|
Layout[2].append(FfsNum)
|
|
Layout[1].append(sum(Layout[2][Idx2:]))
|
|
Idx2 = len(Layout[2])
|
|
Layout[0].append(sum(Layout[1][Idx1:]))
|
|
Idx1 = len(Layout[1])
|
|
Count = sum(Layout[0])
|
|
self.Layout = Layout
|
|
Table = [[' ' for i in range(Count)] for i in range(len(Layout))]
|
|
|
|
Index = 0
|
|
FfsIdx = 0
|
|
FvIdx = 0
|
|
FdIdx = 0
|
|
for Idx0, (Img, FdList) in enumerate(Image):
|
|
FillTable(Table, Index, Layout[0][Idx0], 0, Img)
|
|
for Idx1, (Fd, FvList) in enumerate(FdList):
|
|
FillTable(Table, Index, Layout[1][FdIdx], 1, Fd)
|
|
for Idx2, (Fv, FfsList) in enumerate(FvList):
|
|
FillTable(Table, Index, Layout[2][FvIdx], 2, Fv)
|
|
for Idx3, Ffs in enumerate(FfsList):
|
|
FillTable(Table, Index, Layout[3][FfsIdx], 3, Ffs)
|
|
Index += Layout[3][FfsIdx]
|
|
FfsIdx += 1
|
|
FvIdx += 1
|
|
FdIdx += 1
|
|
|
|
return Table, Layout
|
|
|
|
def GetComponentLineIdx(Level, ObjIdx):
|
|
LineIdx = 0
|
|
LineLen = 0
|
|
for Idx, Each in enumerate(self.Layout[Level]):
|
|
if Idx == ObjIdx:
|
|
LineLen = Each
|
|
break
|
|
LineIdx += Each
|
|
return LineIdx, LineLen
|
|
|
|
def MarkFvInfo(Fv, FvIdx, Lines):
|
|
LineIdx, LineNum = GetComponentLineIdx(2, FvIdx)
|
|
Index = LineIdx + LineNum
|
|
Lines[Index] = Lines[Index] + ' %08X' % Fv.Base
|
|
|
|
Name = '!'.join(self.ImgName)
|
|
Tree = [(Name, [])]
|
|
FdParent = Tree[0][1]
|
|
for Fd in FdList:
|
|
FdParent.append((Normalize(Fd), []))
|
|
FvParent = FdParent[-1][1]
|
|
for Fv in Fd.ChildList:
|
|
FvParent.append((Normalize(Fv), []))
|
|
FfsParent = FvParent[-1][1]
|
|
for Ffs in Fv.ChildList:
|
|
FfsParent.append(Normalize(Ffs))
|
|
|
|
Table, Layout = CalcRows(Tree)
|
|
Rows = len(Table[0])
|
|
|
|
Lines = []
|
|
Header = OutSplitter(' ', self.Header)
|
|
Lines.append(OutSplitter())
|
|
|
|
for Idx in range(Rows):
|
|
Pattern = GetPattern(Layout, Idx)
|
|
Line = OutSplitter(Pattern, [Table[i][Idx] for i in range(4)])
|
|
Lines.append(Line)
|
|
|
|
FfsIdx = 0
|
|
ImgSize = 0
|
|
for Fd in FdList:
|
|
ImgSize += Fd.Length
|
|
|
|
Offset = ImgSize
|
|
TopOffset = Offset
|
|
LineIdx, LineNum = GetComponentLineIdx(3, 0)
|
|
Lines[LineIdx] += ' %08X' % (TopOffset)
|
|
|
|
for Fd in FdList:
|
|
for Fv in Fd.ChildList:
|
|
for Ffs in Fv.ChildList:
|
|
LineIdx, LineNum = GetComponentLineIdx(3, FfsIdx)
|
|
Index = LineIdx + LineNum
|
|
Offset -= Ffs.Length
|
|
Offset = AlignPtrDown(Offset)
|
|
if Ffs == Fv.ChildList[-1]:
|
|
if Fv.Type == 'FV':
|
|
Offset -= Ffs.Offset
|
|
Lines[Index] = Lines[Index] + ' %08X' % (Offset)
|
|
FfsIdx += 1
|
|
|
|
Lines.insert(0, Header)
|
|
|
|
print('\n'.join(Lines))
|
|
|
|
def ReportFd(FvDir, Title, FdNames):
|
|
# Parse all xref
|
|
Reporter = REPORTER(Title)
|
|
Reporter.ParseGuidXrefFile(os.path.join(FvDir, 'Guid.xref'))
|
|
Reporter.ParseDscParameter(os.path.join(
|
|
os.path.dirname(sys.argv[0]), '../Platform.dsc'))
|
|
|
|
# Get FVs
|
|
FvList = []
|
|
for File in os.listdir(FvDir):
|
|
if not File.lower().endswith('fv.txt'):
|
|
continue
|
|
Fv = Reporter.ParseFvTxtFile(os.path.join(FvDir, File))
|
|
FvList.append(Fv)
|
|
|
|
# Get FDs
|
|
FdList = []
|
|
for File in FdNames:
|
|
Fd = Reporter.ParseFdFile(os.path.join(FvDir, File + '.fd'))
|
|
if Fd is not None:
|
|
FdList.append(Fd)
|
|
|
|
# Reverse FD/FV/FFS
|
|
if True:
|
|
FdList.reverse()
|
|
for Fd in FdList:
|
|
Fd.ChildList.reverse()
|
|
for Fv in Fd.ChildList:
|
|
Fv.ChildList.reverse()
|
|
|
|
# Match FV binary into its FV Name/Base
|
|
for Fd in FdList:
|
|
for FvIndex, Fv1 in enumerate(Fd.ChildList):
|
|
if Fv1.Type == 'FSP':
|
|
continue
|
|
Found = False
|
|
for Fv2 in FvList:
|
|
if Fv2.Type != Fv1.Type or Fv2.Length != Fv1.Length:
|
|
continue
|
|
Match = 0
|
|
for FfsIndex, Child2 in enumerate(Fv2.ChildList):
|
|
for Child1 in Fv1.ChildList:
|
|
if Child1.Name == Child2.Name:
|
|
Match += 1
|
|
break
|
|
if Match == FfsIndex + 1:
|
|
Found = True
|
|
break
|
|
if Found:
|
|
if Fv1.Base != 0xFFFFFFFF:
|
|
Fv1.Base = Fv2.Base
|
|
Fv1.Name = Fv2.Name
|
|
else:
|
|
raise Exception("FV in FD could not be identified !")
|
|
|
|
if len(FdList):
|
|
Reporter.Report(FdList)
|
|
print('\n')
|
|
|
|
|
|
def ReportImageLayout(FvDir, ImgPath, ImgList, ImgStart, TopDown):
|
|
|
|
Name = os.path.splitext(os.path.basename(ImgPath))[0]
|
|
Name = Name.upper().replace('_', ' ')
|
|
Reporter = REPORTER(Name, 'IMG')
|
|
|
|
FdList = []
|
|
Offset = 0
|
|
FfsNum = 0
|
|
for Name, Algo, Val, Mode, Pos in ImgList:
|
|
|
|
BaseName = os.path.splitext(Name)[0]
|
|
Fd = IMG_INFO('FD', BaseName)
|
|
|
|
if Algo:
|
|
FileName = BaseName + '.lz'
|
|
Compressed = True
|
|
else:
|
|
FileName = Name
|
|
Compressed = False
|
|
|
|
OrgName = FileName
|
|
OrgSize = Val if Name == 'EMPTY' else os.path.getsize(os.path.join(FvDir, FileName))
|
|
if OrgSize == 0:
|
|
continue
|
|
|
|
if Mode != STITCH_OPS.MODE_FILE_NOP:
|
|
Padded = True
|
|
FileName = BaseName + '.pad'
|
|
else:
|
|
Padded = False
|
|
|
|
Fd.Offset = Offset
|
|
Fd.Length = Val if Name == 'EMPTY' else os.path.getsize(os.path.join(FvDir, FileName))
|
|
Offset += Fd.Length
|
|
|
|
FvName = FileName.replace('.', ',')
|
|
Fv = IMG_INFO('FV', FvName)
|
|
Fv.Offset = 0
|
|
Fv.Length = Fd.Length
|
|
|
|
FileSize = Fd.Length
|
|
OrgSize = AlignPtrUp(OrgSize, 8)
|
|
FfsName = OrgName.replace('.', ',')
|
|
NameList = [FfsName, 'BLANK']
|
|
if Compressed:
|
|
NameList[0] = 'Compressed!' + NameList[0]
|
|
elif Padded:
|
|
pass
|
|
else:
|
|
del NameList[1]
|
|
|
|
if NameList is None:
|
|
NameList = ['IMAGE', 'BLANK']
|
|
|
|
if Pos == STITCH_OPS.MODE_POS_HEAD:
|
|
if FileSize != OrgSize:
|
|
NameList.reverse()
|
|
OrgSize = FileSize - OrgSize
|
|
|
|
for Idx, Name in enumerate(NameList):
|
|
if Idx == 0:
|
|
Len = OrgSize
|
|
Ffs = IMG_INFO('FFS', Name, 0, Len)
|
|
else:
|
|
Len = FileSize - OrgSize
|
|
Ffs = IMG_INFO('FFS', Name, OrgSize, Len)
|
|
if Len:
|
|
Fv.ChildList.append(Ffs)
|
|
FfsNum += 1
|
|
|
|
Fd.ChildList.append(Fv)
|
|
FdList.append(Fd)
|
|
|
|
FinalSize = os.path.getsize(ImgPath)
|
|
if Offset < FinalSize:
|
|
Length = FinalSize - Offset
|
|
Fd = IMG_INFO('FD', 'BLANK', 0, Length)
|
|
Fv = IMG_INFO('FV', 'BLANK', 0, Length)
|
|
Ffs = IMG_INFO('FFS', 'BLANK', 0, Length)
|
|
Fv.ChildList.append(Ffs)
|
|
Fd.ChildList.append(Fv)
|
|
if TopDown:
|
|
FdList.insert(0, Fd)
|
|
else:
|
|
FdList.append(Fd)
|
|
FfsNum += 1
|
|
|
|
Base = None
|
|
|
|
for Fd in FdList:
|
|
for Fv in Fd.ChildList:
|
|
if Base is None:
|
|
if TopDown:
|
|
Base = ImgStart - FinalSize
|
|
else:
|
|
Base = ImgStart
|
|
Fv.Base = Base
|
|
Base += Fv.Length
|
|
|
|
# Reverse FD/FV/FFS
|
|
FdList.reverse()
|
|
for Fd in FdList:
|
|
Fd.ChildList.reverse()
|
|
for Fv in Fd.ChildList:
|
|
Fv.ChildList.reverse()
|
|
|
|
FfsHeight = 6
|
|
MinHeight = len(Reporter.ImgName) + 2
|
|
if FfsNum * FfsHeight <= MinHeight:
|
|
FfsHeight = (MinHeight + FfsNum - 1) // FfsNum
|
|
Reporter.Report(FdList, FfsHeight)
|
|
|
|
def Usage():
|
|
print("Usage: \n\tGenReport FvBuildDir [StitchInput]")
|
|
|
|
def Main():
|
|
|
|
if len(sys.argv) < 2:
|
|
Usage()
|
|
return 1
|
|
|
|
if len(sys.argv) < 3:
|
|
StitchInput = 'ImgStitch.txt'
|
|
else:
|
|
StitchInput = sys.argv[2]
|
|
|
|
FvDir = sys.argv[1]
|
|
ImgLayoutPath = os.path.join(FvDir, StitchInput)
|
|
if not os.path.exists(ImgLayoutPath):
|
|
raise Exception("No layout file '%s' found !" % StitchInput)
|
|
return 0
|
|
|
|
FdIn = open(ImgLayoutPath, 'r')
|
|
Lines = FdIn.readlines()
|
|
FdIn.close()
|
|
|
|
FdReported = False
|
|
FdNames = ['STAGE2', 'STAGE1B', 'STAGE1A']
|
|
for Line in Lines:
|
|
Line = Line.strip()
|
|
Parts = Line.split('=')
|
|
if len(Parts) == 2:
|
|
if Parts[0].strip() == 'BOARD_INFO':
|
|
BrdInfo = ExecAssignment ('BrdInfo', Parts[1])
|
|
Title = 'Flash Layout for Board %s' % BrdInfo[0]
|
|
Padding = (sum(REPORTER.Cols) - len(Title)) // 2
|
|
if Padding < 0:
|
|
Padding = 0
|
|
print('')
|
|
print(' %s%s' % (' ' * Padding, Title))
|
|
print(' %s%s' % (' ' * Padding, '=' * len(Title)))
|
|
if Parts[0].strip() == 'IMAGE_INFO':
|
|
ImgInfo = ExecAssignment ('ImgInfo', Parts[1])
|
|
ImgPath = os.path.join(FvDir, ImgInfo[0])
|
|
Start = ImgInfo[1]
|
|
TopDown = ImgInfo[2]
|
|
if not FdReported:
|
|
for Name in FdNames:
|
|
ReportFd(FvDir, '', [Name])
|
|
FdReported = True
|
|
elif Parts[0].strip() == 'IMAGE_LIST':
|
|
ImgList = ExecAssignment ('ImgList', Parts[1])
|
|
ReportImageLayout(FvDir, ImgPath, ImgList, Start, TopDown)
|
|
print('\n')
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(Main())
|