1## @file
2# generate capsule
3#
4#  Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
5#
6#  SPDX-License-Identifier: BSD-2-Clause-Patent
7#
8
9##
10# Import Modules
11#
12from __future__ import absolute_import
13from .GenFdsGlobalVariable import GenFdsGlobalVariable, FindExtendTool
14from CommonDataClass.FdfClass import CapsuleClassObject
15import Common.LongFilePathOs as os
16from io import BytesIO
17from Common.Misc import SaveFileOnChange, PackGUID
18import uuid
19from struct import pack
20from Common import EdkLogger
21from Common.BuildToolError import GENFDS_ERROR
22from Common.DataType import TAB_LINE_BREAK
23
24WIN_CERT_REVISION = 0x0200
25WIN_CERT_TYPE_EFI_GUID = 0x0EF1
26EFI_CERT_TYPE_PKCS7_GUID = uuid.UUID('{4aafd29d-68df-49ee-8aa9-347d375665a7}')
27EFI_CERT_TYPE_RSA2048_SHA256_GUID = uuid.UUID('{a7717414-c616-4977-9420-844712a735bf}')
28
29## create inf file describes what goes into capsule and call GenFv to generate capsule
30#
31#
32class Capsule (CapsuleClassObject):
33    ## The constructor
34    #
35    #   @param  self        The object pointer
36    #
37    def __init__(self):
38        CapsuleClassObject.__init__(self)
39        # For GenFv
40        self.BlockSize = None
41        # For GenFv
42        self.BlockNum = None
43        self.CapsuleName = None
44
45    ## Generate FMP capsule
46    #
47    #   @retval string      Generated Capsule file path
48    #
49    def GenFmpCapsule(self):
50        #
51        # Generate capsule header
52        # typedef struct {
53        #     EFI_GUID          CapsuleGuid;
54        #     UINT32            HeaderSize;
55        #     UINT32            Flags;
56        #     UINT32            CapsuleImageSize;
57        # } EFI_CAPSULE_HEADER;
58        #
59        Header = BytesIO()
60        #
61        # Use FMP capsule GUID: 6DCBD5ED-E82D-4C44-BDA1-7194199AD92A
62        #
63        Header.write(PackGUID('6DCBD5ED-E82D-4C44-BDA1-7194199AD92A'.split('-')))
64        HdrSize = 0
65        if 'CAPSULE_HEADER_SIZE' in self.TokensDict:
66            Header.write(pack('=I', int(self.TokensDict['CAPSULE_HEADER_SIZE'], 16)))
67            HdrSize = int(self.TokensDict['CAPSULE_HEADER_SIZE'], 16)
68        else:
69            Header.write(pack('=I', 0x20))
70            HdrSize = 0x20
71        Flags = 0
72        if 'CAPSULE_FLAGS' in self.TokensDict:
73            for flag in self.TokensDict['CAPSULE_FLAGS'].split(','):
74                flag = flag.strip()
75                if flag == 'PopulateSystemTable':
76                    Flags |= 0x00010000 | 0x00020000
77                elif flag == 'PersistAcrossReset':
78                    Flags |= 0x00010000
79                elif flag == 'InitiateReset':
80                    Flags |= 0x00040000
81        Header.write(pack('=I', Flags))
82        #
83        # typedef struct {
84        #     UINT32 Version;
85        #     UINT16 EmbeddedDriverCount;
86        #     UINT16 PayloadItemCount;
87        #     // UINT64 ItemOffsetList[];
88        # } EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER;
89        #
90        FwMgrHdr = BytesIO()
91        if 'CAPSULE_HEADER_INIT_VERSION' in self.TokensDict:
92            FwMgrHdr.write(pack('=I', int(self.TokensDict['CAPSULE_HEADER_INIT_VERSION'], 16)))
93        else:
94            FwMgrHdr.write(pack('=I', 0x00000001))
95        FwMgrHdr.write(pack('=HH', len(self.CapsuleDataList), len(self.FmpPayloadList)))
96        FwMgrHdrSize = 4+2+2+8*(len(self.CapsuleDataList)+len(self.FmpPayloadList))
97
98        #
99        # typedef struct _WIN_CERTIFICATE {
100        #   UINT32 dwLength;
101        #   UINT16 wRevision;
102        #   UINT16 wCertificateType;
103        # //UINT8 bCertificate[ANYSIZE_ARRAY];
104        # } WIN_CERTIFICATE;
105        #
106        # typedef struct _WIN_CERTIFICATE_UEFI_GUID {
107        #   WIN_CERTIFICATE Hdr;
108        #   EFI_GUID        CertType;
109        # //UINT8 CertData[ANYSIZE_ARRAY];
110        # } WIN_CERTIFICATE_UEFI_GUID;
111        #
112        # typedef struct {
113        #   UINT64                    MonotonicCount;
114        #   WIN_CERTIFICATE_UEFI_GUID AuthInfo;
115        # } EFI_FIRMWARE_IMAGE_AUTHENTICATION;
116        #
117        # typedef struct _EFI_CERT_BLOCK_RSA_2048_SHA256 {
118        #   EFI_GUID HashType;
119        #   UINT8 PublicKey[256];
120        #   UINT8 Signature[256];
121        # } EFI_CERT_BLOCK_RSA_2048_SHA256;
122        #
123
124        PreSize = FwMgrHdrSize
125        Content = BytesIO()
126        for driver in self.CapsuleDataList:
127            FileName = driver.GenCapsuleSubItem()
128            FwMgrHdr.write(pack('=Q', PreSize))
129            PreSize += os.path.getsize(FileName)
130            File = open(FileName, 'rb')
131            Content.write(File.read())
132            File.close()
133        for fmp in self.FmpPayloadList:
134            if fmp.Existed:
135                FwMgrHdr.write(pack('=Q', PreSize))
136                PreSize += len(fmp.Buffer)
137                Content.write(fmp.Buffer)
138                continue
139            if fmp.ImageFile:
140                for Obj in fmp.ImageFile:
141                    fmp.ImageFile = Obj.GenCapsuleSubItem()
142            if fmp.VendorCodeFile:
143                for Obj in fmp.VendorCodeFile:
144                    fmp.VendorCodeFile = Obj.GenCapsuleSubItem()
145            if fmp.Certificate_Guid:
146                ExternalTool, ExternalOption = FindExtendTool([], GenFdsGlobalVariable.ArchList, fmp.Certificate_Guid)
147                CmdOption = ''
148                CapInputFile = fmp.ImageFile
149                if not os.path.isabs(fmp.ImageFile):
150                    CapInputFile = os.path.join(GenFdsGlobalVariable.WorkSpaceDir, fmp.ImageFile)
151                CapOutputTmp = os.path.join(GenFdsGlobalVariable.FvDir, self.UiCapsuleName) + '.tmp'
152                if ExternalTool is None:
153                    EdkLogger.error("GenFds", GENFDS_ERROR, "No tool found with GUID %s" % fmp.Certificate_Guid)
154                else:
155                    CmdOption += ExternalTool
156                if ExternalOption:
157                    CmdOption = CmdOption + ' ' + ExternalOption
158                CmdOption += ' -e ' + ' --monotonic-count ' + str(fmp.MonotonicCount) + ' -o ' + CapOutputTmp + ' ' + CapInputFile
159                CmdList = CmdOption.split()
160                GenFdsGlobalVariable.CallExternalTool(CmdList, "Failed to generate FMP auth capsule")
161                if uuid.UUID(fmp.Certificate_Guid) == EFI_CERT_TYPE_PKCS7_GUID:
162                    dwLength = 4 + 2 + 2 + 16 + os.path.getsize(CapOutputTmp) - os.path.getsize(CapInputFile)
163                else:
164                    dwLength = 4 + 2 + 2 + 16 + 16 + 256 + 256
165                fmp.ImageFile = CapOutputTmp
166                AuthData = [fmp.MonotonicCount, dwLength, WIN_CERT_REVISION, WIN_CERT_TYPE_EFI_GUID, fmp.Certificate_Guid]
167                fmp.Buffer = fmp.GenCapsuleSubItem(AuthData)
168            else:
169                fmp.Buffer = fmp.GenCapsuleSubItem()
170            FwMgrHdr.write(pack('=Q', PreSize))
171            PreSize += len(fmp.Buffer)
172            Content.write(fmp.Buffer)
173        BodySize = len(FwMgrHdr.getvalue()) + len(Content.getvalue())
174        Header.write(pack('=I', HdrSize + BodySize))
175        #
176        # The real capsule header structure is 28 bytes
177        #
178        Header.write(b'\x00'*(HdrSize-28))
179        Header.write(FwMgrHdr.getvalue())
180        Header.write(Content.getvalue())
181        #
182        # Generate FMP capsule file
183        #
184        CapOutputFile = os.path.join(GenFdsGlobalVariable.FvDir, self.UiCapsuleName) + '.Cap'
185        SaveFileOnChange(CapOutputFile, Header.getvalue(), True)
186        return CapOutputFile
187
188    ## Generate capsule
189    #
190    #   @param  self        The object pointer
191    #   @retval string      Generated Capsule file path
192    #
193    def GenCapsule(self):
194        if self.UiCapsuleName.upper() + 'cap' in GenFdsGlobalVariable.ImageBinDict:
195            return GenFdsGlobalVariable.ImageBinDict[self.UiCapsuleName.upper() + 'cap']
196
197        GenFdsGlobalVariable.InfLogger( "\nGenerate %s Capsule" %self.UiCapsuleName)
198        if ('CAPSULE_GUID' in self.TokensDict and
199            uuid.UUID(self.TokensDict['CAPSULE_GUID']) == uuid.UUID('6DCBD5ED-E82D-4C44-BDA1-7194199AD92A')):
200            return self.GenFmpCapsule()
201
202        CapInfFile = self.GenCapInf()
203        CapInfFile.append("[files]" + TAB_LINE_BREAK)
204        CapFileList = []
205        for CapsuleDataObj in self.CapsuleDataList:
206            CapsuleDataObj.CapsuleName = self.CapsuleName
207            FileName = CapsuleDataObj.GenCapsuleSubItem()
208            CapsuleDataObj.CapsuleName = None
209            CapFileList.append(FileName)
210            CapInfFile.append("EFI_FILE_NAME = " + \
211                                   FileName      + \
212                                   TAB_LINE_BREAK)
213        SaveFileOnChange(self.CapInfFileName, ''.join(CapInfFile), False)
214        #
215        # Call GenFv tool to generate capsule
216        #
217        CapOutputFile = os.path.join(GenFdsGlobalVariable.FvDir, self.UiCapsuleName)
218        CapOutputFile = CapOutputFile + '.Cap'
219        GenFdsGlobalVariable.GenerateFirmwareVolume(
220                                CapOutputFile,
221                                [self.CapInfFileName],
222                                Capsule=True,
223                                FfsList=CapFileList
224                                )
225
226        GenFdsGlobalVariable.VerboseLogger( "\nGenerate %s Capsule Successfully" %self.UiCapsuleName)
227        GenFdsGlobalVariable.SharpCounter = 0
228        GenFdsGlobalVariable.ImageBinDict[self.UiCapsuleName.upper() + 'cap'] = CapOutputFile
229        return CapOutputFile
230
231    ## Generate inf file for capsule
232    #
233    #   @param  self        The object pointer
234    #   @retval file        inf file object
235    #
236    def GenCapInf(self):
237        self.CapInfFileName = os.path.join(GenFdsGlobalVariable.FvDir,
238                                   self.UiCapsuleName +  "_Cap" + '.inf')
239        CapInfFile = []
240
241        CapInfFile.append("[options]" + TAB_LINE_BREAK)
242
243        for Item in self.TokensDict:
244            CapInfFile.append("EFI_"                    + \
245                                  Item                      + \
246                                  ' = '                     + \
247                                  self.TokensDict[Item]     + \
248                                  TAB_LINE_BREAK)
249
250        return CapInfFile
251