[edk2-devel] [PATCH v1] BaseTools/Capsule: Add capsule dependency support

Aaron Li posted 1 patch 1 week ago
Failed in applying to current master (apply log)
BaseTools/Source/Python/Capsule/GenerateCapsule.py               |  62 ++-
BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py | 409 ++++++++++++++++++++
2 files changed, 464 insertions(+), 7 deletions(-)

[edk2-devel] [PATCH v1] BaseTools/Capsule: Add capsule dependency support

Posted by Aaron Li 1 week ago
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2412

Capsule generate tool support encode capsule dependencies through '-j'
command with a JSON file. To enable dependency feature, "Dependencies"
field for each payload in JSON file is required.
The value of "Dependencies" field is C style infix notation expression.
For example:
  "Dependencies":"72E2945A-00DA-448E-9AA7-075AD840F9D4 > 0x00000001"

The relation of Dependency Expression Opcode in UEFI2.8 chap 23.2 and
infix notation expression value is as follows:
+-----------------------------+--------------------------+
| OPCODE                      | INFIX EXPRESSION VALUE   |
+-----------------------------+--------------------------+
| 0x00 (PUSH_GUID)            | {GUID}                   |
| 0x01 (PUSH_VERSION)         | {UINT32}                 |
| 0x02 (DECLEAR_VERSION_NAME} | DECLEAR "{VERSION_NAME}" |
| 0x03 (AND)                  | &&                       |
| 0x04 (OR)                   | ||                       |
| 0x05 (NOT)                  | ~                        |
| 0x06 (TRUE)                 | TRUE                     |
| 0x07 (FALSE)                | FALSE                    |
| 0x08 (EQ)                   | ==                       |
| 0x09 (GT)                   | >                        |
| 0x0A (GTE)                  | >=                       |
| 0x0B (LT)                   | <                        |
| 0x0C (LTE)                  | <=                       |
+-----------------------------+--------------------------+

Cc: Bob Feng <bob.c.feng@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Signed-off-by: Aaron Li <aaron.li@intel.com>
---
 BaseTools/Source/Python/Capsule/GenerateCapsule.py               |  62 ++-
 BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py | 409 ++++++++++++++++++++
 2 files changed, 464 insertions(+), 7 deletions(-)

diff --git a/BaseTools/Source/Python/Capsule/GenerateCapsule.py b/BaseTools/Source/Python/Capsule/GenerateCapsule.py
index 6838beb682..a8de988253 100644
--- a/BaseTools/Source/Python/Capsule/GenerateCapsule.py
+++ b/BaseTools/Source/Python/Capsule/GenerateCapsule.py
@@ -31,6 +31,7 @@ import json
 from Common.Uefi.Capsule.UefiCapsuleHeader import UefiCapsuleHeaderClass
 from Common.Uefi.Capsule.FmpCapsuleHeader  import FmpCapsuleHeaderClass
 from Common.Uefi.Capsule.FmpAuthHeader     import FmpAuthHeaderClass
+from Common.Uefi.Capsule.CapsuleDependency import CapsuleDependencyClass
 from Common.Edk2.Capsule.FmpPayloadHeader  import FmpPayloadHeaderClass
 
 #
@@ -306,6 +307,7 @@ if __name__ == '__main__':
             OpenSslOtherPublicCertFile   = ConvertJsonValue (Config, 'OpenSslOtherPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)
             OpenSslTrustedPublicCertFile = ConvertJsonValue (Config, 'OpenSslTrustedPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)
             SigningToolPath              = ConvertJsonValue (Config, 'SigningToolPath', os.path.expandvars, Required = False, Default = None)
+            DepexExp                     = ConvertJsonValue (Config, 'Dependencies', str, Required = False, Default = None)
 
             #
             # Read binary input file
@@ -330,7 +332,8 @@ if __name__ == '__main__':
                                             OpenSslSignerPrivateCertFile,
                                             OpenSslOtherPublicCertFile,
                                             OpenSslTrustedPublicCertFile,
-                                            SigningToolPath
+                                            SigningToolPath,
+                                            DepexExp
                                             ))
 
     def GenerateOutputJson (PayloadJsonDescriptorList):
@@ -348,7 +351,8 @@ if __name__ == '__main__':
                                   "OpenSslSignerPrivateCertFile": str(PayloadDescriptor.OpenSslSignerPrivateCertFile),
                                   "OpenSslOtherPublicCertFile": str(PayloadDescriptor.OpenSslOtherPublicCertFile),
                                   "OpenSslTrustedPublicCertFile": str(PayloadDescriptor.OpenSslTrustedPublicCertFile),
-                                  "SigningToolPath": str(PayloadDescriptor.SigningToolPath)
+                                  "SigningToolPath": str(PayloadDescriptor.SigningToolPath),
+                                  "Dependencies" : str(PayloadDescriptor.DepexExp)
                               }for PayloadDescriptor in PayloadJsonDescriptorList
                           ]
                       }
@@ -424,7 +428,8 @@ if __name__ == '__main__':
                      OpenSslSignerPrivateCertFile = None,
                      OpenSslOtherPublicCertFile   = None,
                      OpenSslTrustedPublicCertFile = None,
-                     SigningToolPath              = None
+                     SigningToolPath              = None,
+                     DepexExp                     = None
                      ):
             self.Payload                      = Payload
             self.Guid                         = Guid
@@ -438,6 +443,7 @@ if __name__ == '__main__':
             self.OpenSslOtherPublicCertFile   = OpenSslOtherPublicCertFile
             self.OpenSslTrustedPublicCertFile = OpenSslTrustedPublicCertFile
             self.SigningToolPath              = SigningToolPath
+            self.DepexExp                     = DepexExp
 
             self.UseSignTool = self.SignToolPfxFile is not None
             self.UseOpenSsl  = (self.OpenSslSignerPrivateCertFile is not None and
@@ -446,6 +452,7 @@ if __name__ == '__main__':
             self.AnyOpenSsl  = (self.OpenSslSignerPrivateCertFile is not None or
                                 self.OpenSslOtherPublicCertFile is not None or
                                 self.OpenSslTrustedPublicCertFile is not None)
+            self.UseDependency = self.DepexExp is not None
 
         def Validate(self, args):
             if self.UseSignTool and self.AnyOpenSsl:
@@ -544,7 +551,8 @@ if __name__ == '__main__':
                                             args.OpenSslSignerPrivateCertFile,
                                             args.OpenSslOtherPublicCertFile,
                                             args.OpenSslTrustedPublicCertFile,
-                                            args.SigningToolPath
+                                            args.SigningToolPath,
+                                            None
                                             ))
         for SinglePayloadDescriptor in PayloadDescriptorList:
             try:
@@ -564,6 +572,12 @@ if __name__ == '__main__':
             except:
                 print ('GenerateCapsule: error: can not encode FMP Payload Header')
                 sys.exit (1)
+            if SinglePayloadDescriptor.UseDependency:
+                CapsuleDependency.Payload = Result
+                CapsuleDependency.DepexExp = SinglePayloadDescriptor.DepexExp
+                Result = CapsuleDependency.Encode ()
+                if args.Verbose:
+                    CapsuleDependency.DumpInfo ()
             if SinglePayloadDescriptor.UseOpenSsl or SinglePayloadDescriptor.UseSignTool:
                 #
                 # Sign image with 64-bit MonotonicCount appended to end of image
@@ -657,7 +671,8 @@ if __name__ == '__main__':
                                             args.OpenSslSignerPrivateCertFile,
                                             args.OpenSslOtherPublicCertFile,
                                             args.OpenSslTrustedPublicCertFile,
-                                            args.SigningToolPath
+                                            args.SigningToolPath,
+                                            None
                                             ))
         #
         # Perform additional verification on payload descriptors
@@ -700,7 +715,8 @@ if __name__ == '__main__':
                                                                 PayloadDescriptorList[Index].OpenSslSignerPrivateCertFile,
                                                                 PayloadDescriptorList[Index].OpenSslOtherPublicCertFile,
                                                                 PayloadDescriptorList[Index].OpenSslTrustedPublicCertFile,
-                                                                PayloadDescriptorList[Index].SigningToolPath
+                                                                PayloadDescriptorList[Index].SigningToolPath,
+                                                                None
                                                                 ))
                 else:
                     PayloadDescriptorList[0].Payload = FmpCapsuleHeader.GetFmpCapsuleImageHeader (0).Payload
@@ -708,6 +724,7 @@ if __name__ == '__main__':
                         if Index > 0:
                             PayloadDecodeFile = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).Payload
                             PayloadDescriptorList.append (PayloadDescriptor (PayloadDecodeFile,
+                                                            None,
                                                             None,
                                                             None,
                                                             None,
@@ -736,7 +753,8 @@ if __name__ == '__main__':
                                                             PayloadDescriptorList[Index].OpenSslSignerPrivateCertFile,
                                                             PayloadDescriptorList[Index].OpenSslOtherPublicCertFile,
                                                             PayloadDescriptorList[Index].OpenSslTrustedPublicCertFile,
-                                                            PayloadDescriptorList[Index].SigningToolPath
+                                                            PayloadDescriptorList[Index].SigningToolPath,
+                                                            None
                                                             ))
                 JsonIndex = 0
                 for SinglePayloadDescriptor in PayloadDescriptorList:
@@ -782,6 +800,23 @@ if __name__ == '__main__':
                         if args.Verbose:
                             print ('--------')
                             print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')
+
+                    PayloadSignature = struct.unpack ('<I', SinglePayloadDescriptor.Payload[0:4])
+                    if PayloadSignature != FmpPayloadHeader.Signature:
+                        SinglePayloadDescriptor.UseDependency = True
+                        try:
+                            SinglePayloadDescriptor.Payload = CapsuleDependency.Decode (SinglePayloadDescriptor.Payload)
+                            PayloadJsonDescriptorList[JsonIndex].DepexExp = CapsuleDependency.DepexExp
+                            if args.Verbose:
+                                print ('--------')
+                                CapsuleDependency.DumpInfo ()
+                        except Exception as Msg:
+                            print ('GenerateCapsule: error: invalid dependency expression')
+                    else:
+                        if args.Verbose:
+                            print ('--------')
+                            print ('No EFI_FIRMWARE_IMAGE_DEP')
+
                     try:
                         SinglePayloadDescriptor.Payload = FmpPayloadHeader.Decode (SinglePayloadDescriptor.Payload)
                         PayloadJsonDescriptorList[JsonIndex].FwVersion = FmpPayloadHeader.FwVersion
@@ -852,6 +887,18 @@ if __name__ == '__main__':
                     except:
                         print ('--------')
                         print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')
+
+                    PayloadSignature = struct.unpack ('<I', Result[0:4])
+                    if PayloadSignature != FmpPayloadHeader.Signature:
+                        try:
+                            Result = CapsuleDependency.Decode (Result)
+                            print ('--------')
+                            CapsuleDependency.DumpInfo ()
+                        except:
+                            print ('GenerateCapsule: error: invalid dependency expression')
+                    else:
+                        print ('--------')
+                        print ('No EFI_FIRMWARE_IMAGE_DEP')
                     try:
                         Result = FmpPayloadHeader.Decode (Result)
                         print ('--------')
@@ -973,6 +1020,7 @@ if __name__ == '__main__':
     FmpCapsuleHeader  = FmpCapsuleHeaderClass ()
     FmpAuthHeader     = FmpAuthHeaderClass ()
     FmpPayloadHeader  = FmpPayloadHeaderClass ()
+    CapsuleDependency = CapsuleDependencyClass ()
 
     EmbeddedDriverDescriptorList = []
     PayloadDescriptorList = []
diff --git a/BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py b/BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py
new file mode 100644
index 0000000000..74004857a7
--- /dev/null
+++ b/BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py
@@ -0,0 +1,409 @@
+## @file
+# Module that encodes and decodes a capsule dependency.
+#
+# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+import struct
+import json
+import sys
+import uuid
+import re
+
+'''
+CapsuleDependency
+'''
+
+class OpConvert (object):
+    def __init__ (self):
+        # Opcode: (OperandSize, PackSize, PackFmt, EncodeConvert, DecodeConvert)
+        self._DepexOperations = {0x00:    (16, 16, 's', self.Str2Guid, self.Guid2Str),
+                                 0x01:    (4,  1,  'I', self.Str2Uint, self.Uint2Str),
+                                 0x02:    (1,  0,  's', self.Str2Utf8, self.Byte2Str),
+                                 }
+
+    def Str2Uint (self, Data):
+        try:
+            Value = int (Data, 16)
+        except:
+            Message = '{Data} is not a valid integer value.'.format (Data = Data)
+            raise ValueError (Message)
+        if Value < 0 or Value > 0xFFFFFFFF:
+            Message = '{Data} is not an UINT32.'.format (Data = Data)
+            raise ValueError (Message)
+        return Value
+
+    def Uint2Str (self, Data):
+        if Data < 0 or Data > 0xFFFFFFFF:
+            Message = '{Data} is not an UINT32.'.format (Data = Data)
+            raise ValueError (Message)
+        return "0x{Data:08x}".format (Data = Data)
+
+    def Str2Guid (self, Data):
+        try:
+            Guid = uuid.UUID (Data)
+        except:
+            Message = '{Data} is not a valid registry format GUID value.'.format (Data = Data)
+            raise ValueError (Message)
+        return Guid.bytes_le
+
+    def Guid2Str (self, Data):
+        try:
+            Guid = uuid.UUID (bytes_le = Data)
+        except:
+            Message = '{Data} is not a valid binary format GUID value.'.format (Data = Data)
+            raise ValueError (Message)
+        return str (Guid).upper ()
+
+    def Str2Utf8 (self, Data):
+        if isinstance (Data, str):
+            return Data.encode ('utf-8')
+        else:
+            Message = '{Data} is not a valid string.'.format (Data = Data)
+            raise ValueError (Message)
+
+    def Byte2Str (self, Data):
+        if isinstance (Data, bytes):
+            if Data[-1:] == b'\x00':
+                return str (Data[:-1], 'utf-8')
+            else:
+                return str (Data, 'utf-8')
+        else:
+            Message = '{Data} is not a valid binary string.'.format (Data = Data)
+            raise ValueError (Message)
+
+    def OpEncode (self, Opcode, Operand = None):
+        BinTemp = struct.pack ('<b', Opcode)
+        if Opcode <= 0x02 and Operand != None:
+            OperandSize, PackSize, PackFmt, EncodeConvert, DecodeConvert = self._DepexOperations[Opcode]
+            Value = EncodeConvert (Operand)
+            if Opcode == 0x02:
+                PackSize = len (Value) + 1
+            BinTemp += struct.pack ('<{PackSize}{PackFmt}'.format (PackSize = PackSize, PackFmt = PackFmt), Value)
+        return BinTemp
+
+    def OpDecode (self, Buffer):
+        Opcode = struct.unpack ('<b', Buffer[0:1])[0]
+        if Opcode <= 0x02:
+            OperandSize, PackSize, PackFmt, EncodeConvert, DecodeConvert = self._DepexOperations[Opcode]
+            if Opcode == 0x02:
+                try:
+                    PackSize = Buffer[1:].index (b'\x00') + 1
+                    OperandSize = PackSize
+                except:
+                    Message = 'CapsuleDependency: OpConvert: error: decode failed with wrong opcode/string.'
+                    raise ValueError (Message)
+            try:
+                Operand = DecodeConvert (struct.unpack ('<{PackSize}{PackFmt}'.format (PackSize = PackSize, PackFmt = PackFmt), Buffer[1:1+OperandSize])[0])
+            except:
+                Message = 'CapsuleDependency: OpConvert: error: decode failed with unpack failure.'
+                raise ValueError (Message)
+        else:
+            Operand = None
+            OperandSize = 0
+        return (Opcode, Operand, OperandSize)
+
+class CapsuleDependencyClass (object):
+    # //**************************************************************
+    # // Image Attribute - Dependency
+    # //**************************************************************
+    # typedef struct {
+    #   UINT8 Dependencies[];
+    # } EFI_FIRMWARE_IMAGE_DEP
+
+    # {expression operator : [precedence, opcode, type (1:unary/2:binocular)]}
+    _opReference = {'&&':  [2, 0x03, 2],
+                    '||':  [1, 0x04, 2],
+                    '~':   [5, 0x05, 1],
+                    '==':  [3, 0x08, 2],
+                    '>':   [4, 0x09, 2],
+                    '>=':  [4, 0x0A, 2],
+                    '<':   [4, 0x0B, 2],
+                    '<=':  [4, 0x0C, 2],
+                    }
+
+    def __init__ (self):
+        self.Payload              = b''
+        self._DepexExp            = None
+        self._DepexList           = []
+        self._DepexDump           = []
+        self.Depex                = b''
+        self._Valid               = False
+        self._DepexSize           = 0
+        self._opReferenceReverse  = {v[1] : k for k, v in self._opReference.items ()}
+        self.OpConverter          = OpConvert ()
+
+    @property
+    def DepexExp (self):
+        return self._DepexExp
+
+    @DepexExp.setter
+    def DepexExp (self, DepexExp = ''):
+        if isinstance (DepexExp, str):
+            DepexExp = re.sub (r'\n',r' ',DepexExp)
+            DepexExp = re.sub (r'\(',r' ( ',DepexExp)
+            DepexExp = re.sub (r'\)',r' ) ',DepexExp)
+            DepexExp = re.sub (r'~',r' ~ ',DepexExp)
+            self._DepexList = re.findall(r"[^\s\"\']+|\"[^\"]*\"|\'[^\']*\'",DepexExp)
+            self._DepexExp  = " ".join(self._DepexList)
+
+        else:
+            Msg = 'Input Depex Expression is not valid string.'
+            raise ValueError (Msg)
+
+    def IsValidOperator (self, op):
+        return op in self._opReference.keys ()
+
+    def IsValidUnaryOperator (self, op):
+        return op in self._opReference.keys () and self._opReference[op][2] == 1
+
+    def IsValidBinocularOperator (self, op):
+        return op in self._opReference.keys () and self._opReference[op][2] == 2
+
+    def IsValidGuid (self, operand):
+        try:
+            uuid.UUID (operand)
+        except:
+            return False
+        return True
+
+    def IsValidVersion (self, operand):
+        try:
+            Value = int (operand, 16)
+            if Value < 0 or Value > 0xFFFFFFFF:
+                return False
+        except:
+            return False
+        return True
+
+    def IsValidBoolean (self, operand):
+        try:
+            return operand.upper () in ['TRUE', 'FALSE']
+        except:
+            return False
+
+    def IsValidOperand (self, operand):
+        return self.IsValidVersion (operand) or self.IsValidGuid (operand) or self.IsValidBoolean (operand)
+
+    def IsValidString (self, operand):
+        return operand[0] == "\"" and operand[-1] == "\"" and len(operand) >= 2
+
+    # Check if priority of current operater is greater than pervious op
+    def PriorityNotGreater (self, prevOp, currOp):
+        return self._opReference[currOp][0] <= self._opReference[prevOp][0]
+
+    def ValidateDepex (self):
+        OpList = self._DepexList
+
+        i = 0
+        while i < len (OpList):
+            Op = OpList[i]
+
+            if Op == 'DECLARE':
+                i += 1
+                if i >= len (OpList):
+                    Msg = 'No more Operand after {Op}.'.format (Op = OpList[i-1])
+                    raise IndexError (Msg)
+                # Check valid string
+                if not self.IsValidString(OpList[i]):
+                    Msg = '{Operand} after {Op} is not a valid expression input.'.format (Operand = OpList[i], Op = OpList[i-1])
+                    raise ValueError (Msg)
+
+            elif Op == '(':
+                # Expression cannot end with (
+                if i == len (OpList) - 1:
+                    Msg = 'Expression cannot end with \'(\''
+                    raise ValueError (Msg)
+                # The previous op after '(' cannot be a binocular operator
+                if self.IsValidBinocularOperator (OpList[i+1]) :
+                    Msg = '{Op} after \'(\' is not a valid expression input.'.format (Op = OpList[i+1])
+                    raise ValueError (Msg)
+
+            elif Op == ')':
+                # Expression cannot start with )
+                if i == 0:
+                    Msg = 'Expression cannot start with \')\''
+                    raise ValueError (Msg)
+                # The previous op before ')' cannot be an operator
+                if self.IsValidOperator (OpList[i-1]):
+                    Msg = '{Op} before \')\' is not a valid expression input.'.format (Op = OpList[i-1])
+                    raise ValueError (Msg)
+                # The next op after ')' cannot be operand or unary operator
+                if (i + 1) < len (OpList) and (self.IsValidOperand (OpList[i+1]) or self.IsValidUnaryOperator (OpList[i+1])):
+                    Msg = '{Op} after \')\' is not a valid expression input.'.format (Op = OpList[i+1])
+                    raise ValueError (Msg)
+
+            elif self.IsValidOperand (Op):
+                # The next expression of operand cannot be operand or unary operator
+                if (i + 1) < len (OpList) and (self.IsValidOperand (OpList[i+1]) or self.IsValidUnaryOperator (OpList[i+1])):
+                    Msg = '{Op} after {PrevOp} is not a valid expression input.'.format (Op = OpList[i+1], PrevOp = Op)
+                    raise ValueError (Msg)
+
+            elif self.IsValidOperator (Op):
+                # The next op of operator cannot binocular operator
+                if (i + 1) < len (OpList) and self.IsValidBinocularOperator (OpList[i+1]):
+                    Msg = '{Op} after {PrevOp} is not a valid expression input.'.format (Op = OpList[i+1], PrevOp = Op)
+                    raise ValueError (Msg)
+                # The first op can not be binocular operator
+                if i == 0 and self.IsValidBinocularOperator (Op):
+                    Msg = 'Expression cannot start with an operator {Op}.'.format (Op = Op)
+                    raise ValueError (Msg)
+                # The last op can not be operator
+                if i == len (OpList) - 1:
+                    Msg = 'Expression cannot ended with an operator {Op}.'.format (Op = Op)
+                    raise ValueError (Msg)
+                # The next op of unary operator cannot be guid / version
+                if self.IsValidUnaryOperator (Op) and (self.IsValidGuid (OpList[i+1]) or self.IsValidVersion (OpList[i+1])):
+                    Msg = '{Op} after {PrevOp} is not a valid expression input.'.format (Op = OpList[i+1], PrevOp = Op)
+                    raise ValueError (Msg)
+
+            else:
+                Msg = '{Op} is not a valid expression input.'.format (Op = Op)
+                raise ValueError (Msg)
+            i += 1
+
+    def Encode (self):
+        # initialize
+        self.Depex = b''
+        self._DepexDump = []
+        OperandStack = []
+        OpeartorStack = []
+        OpList = self._DepexList
+
+        self.ValidateDepex ()
+
+        # convert
+        i = 0
+        while i < len (OpList):
+            Op = OpList[i]
+            if Op == 'DECLARE':
+                # This declare next expression value is a VERSION_STRING
+                i += 1
+                self.Depex += self.OpConverter.OpEncode (0x02, OpList[i][1:-1])
+
+            elif Op == '(':
+                OpeartorStack.append (Op)
+
+            elif Op == ')':
+                while (OpeartorStack and OpeartorStack[-1] != '('):
+                    Operator = OpeartorStack.pop ()
+                    self.Depex += self.OpConverter.OpEncode (self._opReference[Operator][1])
+                try:
+                    OpeartorStack.pop () # pop out '('
+                except:
+                    Msg = 'Pop out \'(\' failed, too many \')\''
+                    raise ValueError (Msg)
+
+            elif self.IsValidGuid (Op):
+                if not OperandStack:
+                    OperandStack.append (self.OpConverter.OpEncode (0x00, Op))
+                else:
+                    # accroding to uefi spec 2.8, the guid/version operands is a reversed order in firmware comparison.
+                    self.Depex += self.OpConverter.OpEncode (0x00, Op)
+                    self.Depex += OperandStack.pop ()
+
+            elif self.IsValidVersion (Op):
+                if not OperandStack:
+                    OperandStack.append (self.OpConverter.OpEncode (0x01, Op))
+                else:
+                    # accroding to uefi spec 2.8, the guid/version operands is a reversed order in firmware comparison.
+                    self.Depex += self.OpConverter.OpEncode (0x01, Op)
+                    self.Depex += OperandStack.pop ()
+
+            elif self.IsValidBoolean (Op):
+                if Op.upper () == 'FALSE':
+                    self.Depex += self.OpConverter.OpEncode (0x07)
+                elif Op.upper () == 'TRUE':
+                    self.Depex += self.OpConverter.OpEncode (0x06)
+
+            elif self.IsValidOperator (Op):
+                while (OpeartorStack and OpeartorStack[-1] != '(' and self.PriorityNotGreater (OpeartorStack[-1], Op)):
+                    Operator = OpeartorStack.pop ()
+                    self.Depex += self.OpConverter.OpEncode (self._opReference[Operator][1])
+                OpeartorStack.append (Op)
+
+            i += 1
+
+        while OpeartorStack:
+            Operator = OpeartorStack.pop ()
+            if Operator == '(':
+                Msg = 'Too many \'(\'.'
+                raise ValueError (Msg)
+            self.Depex += self.OpConverter.OpEncode (self._opReference[Operator][1])
+        self.Depex += self.OpConverter.OpEncode (0x0D)
+
+        self._Valid = True
+        self._DepexSize = len (self.Depex)
+        return self.Depex + self.Payload
+
+    def Decode (self, Buffer):
+        # initialize
+        self.Depex = Buffer
+        OperandStack = []
+        DepexLen = 0
+
+        while True:
+            Opcode, Operand, OperandSize = self.OpConverter.OpDecode (Buffer[DepexLen:])
+            DepexLen += OperandSize + 1
+
+            if Opcode == 0x0D:
+                break
+
+            elif Opcode == 0x02:
+                if not OperandStack:
+                    OperandStack.append ('DECLARE \"{String}\"'.format (String = Operand))
+                else:
+                    PrevOperand = OperandStack.pop ()
+                    OperandStack.append ('{Operand} DECLARE \"{String}\"'.format (Operand = PrevOperand, String = Operand))
+
+            elif Opcode in [0x00, 0x01]:
+                OperandStack.append (Operand)
+
+            elif Opcode == 0x06:
+                OperandStack.append ('TRUE')
+
+            elif Opcode == 0x07:
+                OperandStack.append ('FALSE')
+
+            elif self.IsValidOperator (self._opReferenceReverse[Opcode]):
+                Operator = self._opReferenceReverse[Opcode]
+                if self.IsValidUnaryOperator (self._opReferenceReverse[Opcode]) and len (OperandStack) >= 1:
+                    Oprand = OperandStack.pop ()
+                    OperandStack.append (' ( {Operator} {Oprand} )'.format (Operator = Operator, Oprand = Oprand))
+                elif self.IsValidBinocularOperator (self._opReferenceReverse[Opcode]) and len (OperandStack) >= 2:
+                    Oprand1 = OperandStack.pop ()
+                    Oprand2 = OperandStack.pop ()
+                    OperandStack.append (' ( {Oprand1} {Operator} {Oprand2} )'.format (Operator = Operator, Oprand1 = Oprand1, Oprand2 = Oprand2))
+                else:
+                    Msg = 'No enough Operands for {Opcode:02X}.'.format (Opcode = Opcode)
+                    raise ValueError (Msg)
+
+            else:
+                Msg = '{Opcode:02X} is not a valid OpCode.'.format (Opcode = Opcode)
+                raise ValueError (Msg)
+
+        self.DepexExp = OperandStack[0].strip (' ')
+        self.Payload = Buffer[DepexLen:]
+        self._Valid = True
+        self._DepexSize = DepexLen
+        return self.Payload
+
+
+    def DumpInfo (self):
+        DepexLen = 0
+        Opcode = None
+        Buffer = self.Depex
+
+        if self._Valid == True:
+            print ('EFI_FIRMWARE_IMAGE_DEP.Dependencies = {')
+            while Opcode != 0x0D:
+                Opcode, Operand, OperandSize = self.OpConverter.OpDecode (Buffer[DepexLen:])
+                DepexLen += OperandSize + 1
+                if Operand:
+                    print ('    {Opcode:02X}, {Operand},'.format (Opcode = Opcode, Operand = Operand))
+                else:
+                    print ('    {Opcode:02X},'.format (Opcode = Opcode))
+            print ('}')
+
+            print ('sizeof (EFI_FIRMWARE_IMAGE_DEP.Dependencies)    = {Size:08X}'.format (Size = self._DepexSize))
+            print ('sizeof (Payload)                                = {Size:08X}'.format (Size = len (self.Payload)))
-- 
2.18.0.windows.1


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.

View/Reply Online (#53118): https://edk2.groups.io/g/devel/message/53118
Mute This Topic: https://groups.io/mt/69594821/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub  [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-

Re: [edk2-devel] [PATCH v1] BaseTools/Capsule: Add capsule dependency support

Posted by Bob Feng 6 days ago
Reviewed-by: Bob Feng <bob.c.feng@intel.com>

-----Original Message-----
From: Li, Aaron 
Sent: Friday, January 10, 2020 9:58 AM
To: devel@edk2.groups.io
Cc: Feng, Bob C <bob.c.feng@intel.com>; Gao, Liming <liming.gao@intel.com>
Subject: [PATCH v1] BaseTools/Capsule: Add capsule dependency support

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=2412

Capsule generate tool support encode capsule dependencies through '-j'
command with a JSON file. To enable dependency feature, "Dependencies"
field for each payload in JSON file is required.
The value of "Dependencies" field is C style infix notation expression.
For example:
  "Dependencies":"72E2945A-00DA-448E-9AA7-075AD840F9D4 > 0x00000001"

The relation of Dependency Expression Opcode in UEFI2.8 chap 23.2 and infix notation expression value is as follows:
+-----------------------------+--------------------------+
| OPCODE                      | INFIX EXPRESSION VALUE   |
+-----------------------------+--------------------------+
| 0x00 (PUSH_GUID)            | {GUID}                   |
| 0x01 (PUSH_VERSION)         | {UINT32}                 |
| 0x02 (DECLEAR_VERSION_NAME} | DECLEAR "{VERSION_NAME}" |
| 0x03 (AND)                  | &&                       |
| 0x04 (OR)                   | ||                       |
| 0x05 (NOT)                  | ~                        |
| 0x06 (TRUE)                 | TRUE                     |
| 0x07 (FALSE)                | FALSE                    |
| 0x08 (EQ)                   | ==                       |
| 0x09 (GT)                   | >                        |
| 0x0A (GTE)                  | >=                       |
| 0x0B (LT)                   | <                        |
| 0x0C (LTE)                  | <=                       |
+-----------------------------+--------------------------+

Cc: Bob Feng <bob.c.feng@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Signed-off-by: Aaron Li <aaron.li@intel.com>
---
 BaseTools/Source/Python/Capsule/GenerateCapsule.py               |  62 ++-
 BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py | 409 ++++++++++++++++++++
 2 files changed, 464 insertions(+), 7 deletions(-)

diff --git a/BaseTools/Source/Python/Capsule/GenerateCapsule.py b/BaseTools/Source/Python/Capsule/GenerateCapsule.py
index 6838beb682..a8de988253 100644
--- a/BaseTools/Source/Python/Capsule/GenerateCapsule.py
+++ b/BaseTools/Source/Python/Capsule/GenerateCapsule.py
@@ -31,6 +31,7 @@ import json
 from Common.Uefi.Capsule.UefiCapsuleHeader import UefiCapsuleHeaderClass  from Common.Uefi.Capsule.FmpCapsuleHeader  import FmpCapsuleHeaderClass
 from Common.Uefi.Capsule.FmpAuthHeader     import FmpAuthHeaderClass
+from Common.Uefi.Capsule.CapsuleDependency import 
+CapsuleDependencyClass
 from Common.Edk2.Capsule.FmpPayloadHeader  import FmpPayloadHeaderClass
 
 #
@@ -306,6 +307,7 @@ if __name__ == '__main__':
             OpenSslOtherPublicCertFile   = ConvertJsonValue (Config, 'OpenSslOtherPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)
             OpenSslTrustedPublicCertFile = ConvertJsonValue (Config, 'OpenSslTrustedPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)
             SigningToolPath              = ConvertJsonValue (Config, 'SigningToolPath', os.path.expandvars, Required = False, Default = None)
+            DepexExp                     = ConvertJsonValue (Config, 'Dependencies', str, Required = False, Default = None)
 
             #
             # Read binary input file
@@ -330,7 +332,8 @@ if __name__ == '__main__':
                                             OpenSslSignerPrivateCertFile,
                                             OpenSslOtherPublicCertFile,
                                             OpenSslTrustedPublicCertFile,
-                                            SigningToolPath
+                                            SigningToolPath,
+                                            DepexExp
                                             ))
 
     def GenerateOutputJson (PayloadJsonDescriptorList):
@@ -348,7 +351,8 @@ if __name__ == '__main__':
                                   "OpenSslSignerPrivateCertFile": str(PayloadDescriptor.OpenSslSignerPrivateCertFile),
                                   "OpenSslOtherPublicCertFile": str(PayloadDescriptor.OpenSslOtherPublicCertFile),
                                   "OpenSslTrustedPublicCertFile": str(PayloadDescriptor.OpenSslTrustedPublicCertFile),
-                                  "SigningToolPath": str(PayloadDescriptor.SigningToolPath)
+                                  "SigningToolPath": str(PayloadDescriptor.SigningToolPath),
+                                  "Dependencies" : 
+ str(PayloadDescriptor.DepexExp)
                               }for PayloadDescriptor in PayloadJsonDescriptorList
                           ]
                       }
@@ -424,7 +428,8 @@ if __name__ == '__main__':
                      OpenSslSignerPrivateCertFile = None,
                      OpenSslOtherPublicCertFile   = None,
                      OpenSslTrustedPublicCertFile = None,
-                     SigningToolPath              = None
+                     SigningToolPath              = None,
+                     DepexExp                     = None
                      ):
             self.Payload                      = Payload
             self.Guid                         = Guid
@@ -438,6 +443,7 @@ if __name__ == '__main__':
             self.OpenSslOtherPublicCertFile   = OpenSslOtherPublicCertFile
             self.OpenSslTrustedPublicCertFile = OpenSslTrustedPublicCertFile
             self.SigningToolPath              = SigningToolPath
+            self.DepexExp                     = DepexExp
 
             self.UseSignTool = self.SignToolPfxFile is not None
             self.UseOpenSsl  = (self.OpenSslSignerPrivateCertFile is not None and @@ -446,6 +452,7 @@ if __name__ == '__main__':
             self.AnyOpenSsl  = (self.OpenSslSignerPrivateCertFile is not None or
                                 self.OpenSslOtherPublicCertFile is not None or
                                 self.OpenSslTrustedPublicCertFile is not None)
+            self.UseDependency = self.DepexExp is not None
 
         def Validate(self, args):
             if self.UseSignTool and self.AnyOpenSsl:
@@ -544,7 +551,8 @@ if __name__ == '__main__':
                                             args.OpenSslSignerPrivateCertFile,
                                             args.OpenSslOtherPublicCertFile,
                                             args.OpenSslTrustedPublicCertFile,
-                                            args.SigningToolPath
+                                            args.SigningToolPath,
+                                            None
                                             ))
         for SinglePayloadDescriptor in PayloadDescriptorList:
             try:
@@ -564,6 +572,12 @@ if __name__ == '__main__':
             except:
                 print ('GenerateCapsule: error: can not encode FMP Payload Header')
                 sys.exit (1)
+            if SinglePayloadDescriptor.UseDependency:
+                CapsuleDependency.Payload = Result
+                CapsuleDependency.DepexExp = SinglePayloadDescriptor.DepexExp
+                Result = CapsuleDependency.Encode ()
+                if args.Verbose:
+                    CapsuleDependency.DumpInfo ()
             if SinglePayloadDescriptor.UseOpenSsl or SinglePayloadDescriptor.UseSignTool:
                 #
                 # Sign image with 64-bit MonotonicCount appended to end of image @@ -657,7 +671,8 @@ if __name__ == '__main__':
                                             args.OpenSslSignerPrivateCertFile,
                                             args.OpenSslOtherPublicCertFile,
                                             args.OpenSslTrustedPublicCertFile,
-                                            args.SigningToolPath
+                                            args.SigningToolPath,
+                                            None
                                             ))
         #
         # Perform additional verification on payload descriptors @@ -700,7 +715,8 @@ if __name__ == '__main__':
                                                                 PayloadDescriptorList[Index].OpenSslSignerPrivateCertFile,
                                                                 PayloadDescriptorList[Index].OpenSslOtherPublicCertFile,
                                                                 PayloadDescriptorList[Index].OpenSslTrustedPublicCertFile,
-                                                                PayloadDescriptorList[Index].SigningToolPath
+                                                                PayloadDescriptorList[Index].SigningToolPath,
+                                                                None
                                                                 ))
                 else:
                     PayloadDescriptorList[0].Payload = FmpCapsuleHeader.GetFmpCapsuleImageHeader (0).Payload @@ -708,6 +724,7 @@ if __name__ == '__main__':
                         if Index > 0:
                             PayloadDecodeFile = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).Payload
                             PayloadDescriptorList.append (PayloadDescriptor (PayloadDecodeFile,
+                                                            None,
                                                             None,
                                                             None,
                                                             None, @@ -736,7 +753,8 @@ if __name__ == '__main__':
                                                             PayloadDescriptorList[Index].OpenSslSignerPrivateCertFile,
                                                             PayloadDescriptorList[Index].OpenSslOtherPublicCertFile,
                                                             PayloadDescriptorList[Index].OpenSslTrustedPublicCertFile,
-                                                            PayloadDescriptorList[Index].SigningToolPath
+                                                            PayloadDescriptorList[Index].SigningToolPath,
+                                                            None
                                                             ))
                 JsonIndex = 0
                 for SinglePayloadDescriptor in PayloadDescriptorList:
@@ -782,6 +800,23 @@ if __name__ == '__main__':
                         if args.Verbose:
                             print ('--------')
                             print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')
+
+                    PayloadSignature = struct.unpack ('<I', SinglePayloadDescriptor.Payload[0:4])
+                    if PayloadSignature != FmpPayloadHeader.Signature:
+                        SinglePayloadDescriptor.UseDependency = True
+                        try:
+                            SinglePayloadDescriptor.Payload = CapsuleDependency.Decode (SinglePayloadDescriptor.Payload)
+                            PayloadJsonDescriptorList[JsonIndex].DepexExp = CapsuleDependency.DepexExp
+                            if args.Verbose:
+                                print ('--------')
+                                CapsuleDependency.DumpInfo ()
+                        except Exception as Msg:
+                            print ('GenerateCapsule: error: invalid dependency expression')
+                    else:
+                        if args.Verbose:
+                            print ('--------')
+                            print ('No EFI_FIRMWARE_IMAGE_DEP')
+
                     try:
                         SinglePayloadDescriptor.Payload = FmpPayloadHeader.Decode (SinglePayloadDescriptor.Payload)
                         PayloadJsonDescriptorList[JsonIndex].FwVersion = FmpPayloadHeader.FwVersion @@ -852,6 +887,18 @@ if __name__ == '__main__':
                     except:
                         print ('--------')
                         print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')
+
+                    PayloadSignature = struct.unpack ('<I', Result[0:4])
+                    if PayloadSignature != FmpPayloadHeader.Signature:
+                        try:
+                            Result = CapsuleDependency.Decode (Result)
+                            print ('--------')
+                            CapsuleDependency.DumpInfo ()
+                        except:
+                            print ('GenerateCapsule: error: invalid dependency expression')
+                    else:
+                        print ('--------')
+                        print ('No EFI_FIRMWARE_IMAGE_DEP')
                     try:
                         Result = FmpPayloadHeader.Decode (Result)
                         print ('--------') @@ -973,6 +1020,7 @@ if __name__ == '__main__':
     FmpCapsuleHeader  = FmpCapsuleHeaderClass ()
     FmpAuthHeader     = FmpAuthHeaderClass ()
     FmpPayloadHeader  = FmpPayloadHeaderClass ()
+    CapsuleDependency = CapsuleDependencyClass ()
 
     EmbeddedDriverDescriptorList = []
     PayloadDescriptorList = []
diff --git a/BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py b/BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py
new file mode 100644
index 0000000000..74004857a7
--- /dev/null
+++ b/BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py
@@ -0,0 +1,409 @@
+## @file
+# Module that encodes and decodes a capsule dependency.
+#
+# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR> # 
+SPDX-License-Identifier: BSD-2-Clause-Patent # import struct import 
+json import sys import uuid import re
+
+'''
+CapsuleDependency
+'''
+
+class OpConvert (object):
+    def __init__ (self):
+        # Opcode: (OperandSize, PackSize, PackFmt, EncodeConvert, DecodeConvert)
+        self._DepexOperations = {0x00:    (16, 16, 's', self.Str2Guid, self.Guid2Str),
+                                 0x01:    (4,  1,  'I', self.Str2Uint, self.Uint2Str),
+                                 0x02:    (1,  0,  's', self.Str2Utf8, self.Byte2Str),
+                                 }
+
+    def Str2Uint (self, Data):
+        try:
+            Value = int (Data, 16)
+        except:
+            Message = '{Data} is not a valid integer value.'.format (Data = Data)
+            raise ValueError (Message)
+        if Value < 0 or Value > 0xFFFFFFFF:
+            Message = '{Data} is not an UINT32.'.format (Data = Data)
+            raise ValueError (Message)
+        return Value
+
+    def Uint2Str (self, Data):
+        if Data < 0 or Data > 0xFFFFFFFF:
+            Message = '{Data} is not an UINT32.'.format (Data = Data)
+            raise ValueError (Message)
+        return "0x{Data:08x}".format (Data = Data)
+
+    def Str2Guid (self, Data):
+        try:
+            Guid = uuid.UUID (Data)
+        except:
+            Message = '{Data} is not a valid registry format GUID value.'.format (Data = Data)
+            raise ValueError (Message)
+        return Guid.bytes_le
+
+    def Guid2Str (self, Data):
+        try:
+            Guid = uuid.UUID (bytes_le = Data)
+        except:
+            Message = '{Data} is not a valid binary format GUID value.'.format (Data = Data)
+            raise ValueError (Message)
+        return str (Guid).upper ()
+
+    def Str2Utf8 (self, Data):
+        if isinstance (Data, str):
+            return Data.encode ('utf-8')
+        else:
+            Message = '{Data} is not a valid string.'.format (Data = Data)
+            raise ValueError (Message)
+
+    def Byte2Str (self, Data):
+        if isinstance (Data, bytes):
+            if Data[-1:] == b'\x00':
+                return str (Data[:-1], 'utf-8')
+            else:
+                return str (Data, 'utf-8')
+        else:
+            Message = '{Data} is not a valid binary string.'.format (Data = Data)
+            raise ValueError (Message)
+
+    def OpEncode (self, Opcode, Operand = None):
+        BinTemp = struct.pack ('<b', Opcode)
+        if Opcode <= 0x02 and Operand != None:
+            OperandSize, PackSize, PackFmt, EncodeConvert, DecodeConvert = self._DepexOperations[Opcode]
+            Value = EncodeConvert (Operand)
+            if Opcode == 0x02:
+                PackSize = len (Value) + 1
+            BinTemp += struct.pack ('<{PackSize}{PackFmt}'.format (PackSize = PackSize, PackFmt = PackFmt), Value)
+        return BinTemp
+
+    def OpDecode (self, Buffer):
+        Opcode = struct.unpack ('<b', Buffer[0:1])[0]
+        if Opcode <= 0x02:
+            OperandSize, PackSize, PackFmt, EncodeConvert, DecodeConvert = self._DepexOperations[Opcode]
+            if Opcode == 0x02:
+                try:
+                    PackSize = Buffer[1:].index (b'\x00') + 1
+                    OperandSize = PackSize
+                except:
+                    Message = 'CapsuleDependency: OpConvert: error: decode failed with wrong opcode/string.'
+                    raise ValueError (Message)
+            try:
+                Operand = DecodeConvert (struct.unpack ('<{PackSize}{PackFmt}'.format (PackSize = PackSize, PackFmt = PackFmt), Buffer[1:1+OperandSize])[0])
+            except:
+                Message = 'CapsuleDependency: OpConvert: error: decode failed with unpack failure.'
+                raise ValueError (Message)
+        else:
+            Operand = None
+            OperandSize = 0
+        return (Opcode, Operand, OperandSize)
+
+class CapsuleDependencyClass (object):
+    # //**************************************************************
+    # // Image Attribute - Dependency
+    # //**************************************************************
+    # typedef struct {
+    #   UINT8 Dependencies[];
+    # } EFI_FIRMWARE_IMAGE_DEP
+
+    # {expression operator : [precedence, opcode, type (1:unary/2:binocular)]}
+    _opReference = {'&&':  [2, 0x03, 2],
+                    '||':  [1, 0x04, 2],
+                    '~':   [5, 0x05, 1],
+                    '==':  [3, 0x08, 2],
+                    '>':   [4, 0x09, 2],
+                    '>=':  [4, 0x0A, 2],
+                    '<':   [4, 0x0B, 2],
+                    '<=':  [4, 0x0C, 2],
+                    }
+
+    def __init__ (self):
+        self.Payload              = b''
+        self._DepexExp            = None
+        self._DepexList           = []
+        self._DepexDump           = []
+        self.Depex                = b''
+        self._Valid               = False
+        self._DepexSize           = 0
+        self._opReferenceReverse  = {v[1] : k for k, v in self._opReference.items ()}
+        self.OpConverter          = OpConvert ()
+
+    @property
+    def DepexExp (self):
+        return self._DepexExp
+
+    @DepexExp.setter
+    def DepexExp (self, DepexExp = ''):
+        if isinstance (DepexExp, str):
+            DepexExp = re.sub (r'\n',r' ',DepexExp)
+            DepexExp = re.sub (r'\(',r' ( ',DepexExp)
+            DepexExp = re.sub (r'\)',r' ) ',DepexExp)
+            DepexExp = re.sub (r'~',r' ~ ',DepexExp)
+            self._DepexList = re.findall(r"[^\s\"\']+|\"[^\"]*\"|\'[^\']*\'",DepexExp)
+            self._DepexExp  = " ".join(self._DepexList)
+
+        else:
+            Msg = 'Input Depex Expression is not valid string.'
+            raise ValueError (Msg)
+
+    def IsValidOperator (self, op):
+        return op in self._opReference.keys ()
+
+    def IsValidUnaryOperator (self, op):
+        return op in self._opReference.keys () and 
+ self._opReference[op][2] == 1
+
+    def IsValidBinocularOperator (self, op):
+        return op in self._opReference.keys () and 
+ self._opReference[op][2] == 2
+
+    def IsValidGuid (self, operand):
+        try:
+            uuid.UUID (operand)
+        except:
+            return False
+        return True
+
+    def IsValidVersion (self, operand):
+        try:
+            Value = int (operand, 16)
+            if Value < 0 or Value > 0xFFFFFFFF:
+                return False
+        except:
+            return False
+        return True
+
+    def IsValidBoolean (self, operand):
+        try:
+            return operand.upper () in ['TRUE', 'FALSE']
+        except:
+            return False
+
+    def IsValidOperand (self, operand):
+        return self.IsValidVersion (operand) or self.IsValidGuid 
+ (operand) or self.IsValidBoolean (operand)
+
+    def IsValidString (self, operand):
+        return operand[0] == "\"" and operand[-1] == "\"" and 
+ len(operand) >= 2
+
+    # Check if priority of current operater is greater than pervious op
+    def PriorityNotGreater (self, prevOp, currOp):
+        return self._opReference[currOp][0] <= 
+ self._opReference[prevOp][0]
+
+    def ValidateDepex (self):
+        OpList = self._DepexList
+
+        i = 0
+        while i < len (OpList):
+            Op = OpList[i]
+
+            if Op == 'DECLARE':
+                i += 1
+                if i >= len (OpList):
+                    Msg = 'No more Operand after {Op}.'.format (Op = OpList[i-1])
+                    raise IndexError (Msg)
+                # Check valid string
+                if not self.IsValidString(OpList[i]):
+                    Msg = '{Operand} after {Op} is not a valid expression input.'.format (Operand = OpList[i], Op = OpList[i-1])
+                    raise ValueError (Msg)
+
+            elif Op == '(':
+                # Expression cannot end with (
+                if i == len (OpList) - 1:
+                    Msg = 'Expression cannot end with \'(\''
+                    raise ValueError (Msg)
+                # The previous op after '(' cannot be a binocular operator
+                if self.IsValidBinocularOperator (OpList[i+1]) :
+                    Msg = '{Op} after \'(\' is not a valid expression input.'.format (Op = OpList[i+1])
+                    raise ValueError (Msg)
+
+            elif Op == ')':
+                # Expression cannot start with )
+                if i == 0:
+                    Msg = 'Expression cannot start with \')\''
+                    raise ValueError (Msg)
+                # The previous op before ')' cannot be an operator
+                if self.IsValidOperator (OpList[i-1]):
+                    Msg = '{Op} before \')\' is not a valid expression input.'.format (Op = OpList[i-1])
+                    raise ValueError (Msg)
+                # The next op after ')' cannot be operand or unary operator
+                if (i + 1) < len (OpList) and (self.IsValidOperand (OpList[i+1]) or self.IsValidUnaryOperator (OpList[i+1])):
+                    Msg = '{Op} after \')\' is not a valid expression input.'.format (Op = OpList[i+1])
+                    raise ValueError (Msg)
+
+            elif self.IsValidOperand (Op):
+                # The next expression of operand cannot be operand or unary operator
+                if (i + 1) < len (OpList) and (self.IsValidOperand (OpList[i+1]) or self.IsValidUnaryOperator (OpList[i+1])):
+                    Msg = '{Op} after {PrevOp} is not a valid expression input.'.format (Op = OpList[i+1], PrevOp = Op)
+                    raise ValueError (Msg)
+
+            elif self.IsValidOperator (Op):
+                # The next op of operator cannot binocular operator
+                if (i + 1) < len (OpList) and self.IsValidBinocularOperator (OpList[i+1]):
+                    Msg = '{Op} after {PrevOp} is not a valid expression input.'.format (Op = OpList[i+1], PrevOp = Op)
+                    raise ValueError (Msg)
+                # The first op can not be binocular operator
+                if i == 0 and self.IsValidBinocularOperator (Op):
+                    Msg = 'Expression cannot start with an operator {Op}.'.format (Op = Op)
+                    raise ValueError (Msg)
+                # The last op can not be operator
+                if i == len (OpList) - 1:
+                    Msg = 'Expression cannot ended with an operator {Op}.'.format (Op = Op)
+                    raise ValueError (Msg)
+                # The next op of unary operator cannot be guid / version
+                if self.IsValidUnaryOperator (Op) and (self.IsValidGuid (OpList[i+1]) or self.IsValidVersion (OpList[i+1])):
+                    Msg = '{Op} after {PrevOp} is not a valid expression input.'.format (Op = OpList[i+1], PrevOp = Op)
+                    raise ValueError (Msg)
+
+            else:
+                Msg = '{Op} is not a valid expression input.'.format (Op = Op)
+                raise ValueError (Msg)
+            i += 1
+
+    def Encode (self):
+        # initialize
+        self.Depex = b''
+        self._DepexDump = []
+        OperandStack = []
+        OpeartorStack = []
+        OpList = self._DepexList
+
+        self.ValidateDepex ()
+
+        # convert
+        i = 0
+        while i < len (OpList):
+            Op = OpList[i]
+            if Op == 'DECLARE':
+                # This declare next expression value is a VERSION_STRING
+                i += 1
+                self.Depex += self.OpConverter.OpEncode (0x02, 
+ OpList[i][1:-1])
+
+            elif Op == '(':
+                OpeartorStack.append (Op)
+
+            elif Op == ')':
+                while (OpeartorStack and OpeartorStack[-1] != '('):
+                    Operator = OpeartorStack.pop ()
+                    self.Depex += self.OpConverter.OpEncode (self._opReference[Operator][1])
+                try:
+                    OpeartorStack.pop () # pop out '('
+                except:
+                    Msg = 'Pop out \'(\' failed, too many \')\''
+                    raise ValueError (Msg)
+
+            elif self.IsValidGuid (Op):
+                if not OperandStack:
+                    OperandStack.append (self.OpConverter.OpEncode (0x00, Op))
+                else:
+                    # accroding to uefi spec 2.8, the guid/version operands is a reversed order in firmware comparison.
+                    self.Depex += self.OpConverter.OpEncode (0x00, Op)
+                    self.Depex += OperandStack.pop ()
+
+            elif self.IsValidVersion (Op):
+                if not OperandStack:
+                    OperandStack.append (self.OpConverter.OpEncode (0x01, Op))
+                else:
+                    # accroding to uefi spec 2.8, the guid/version operands is a reversed order in firmware comparison.
+                    self.Depex += self.OpConverter.OpEncode (0x01, Op)
+                    self.Depex += OperandStack.pop ()
+
+            elif self.IsValidBoolean (Op):
+                if Op.upper () == 'FALSE':
+                    self.Depex += self.OpConverter.OpEncode (0x07)
+                elif Op.upper () == 'TRUE':
+                    self.Depex += self.OpConverter.OpEncode (0x06)
+
+            elif self.IsValidOperator (Op):
+                while (OpeartorStack and OpeartorStack[-1] != '(' and self.PriorityNotGreater (OpeartorStack[-1], Op)):
+                    Operator = OpeartorStack.pop ()
+                    self.Depex += self.OpConverter.OpEncode (self._opReference[Operator][1])
+                OpeartorStack.append (Op)
+
+            i += 1
+
+        while OpeartorStack:
+            Operator = OpeartorStack.pop ()
+            if Operator == '(':
+                Msg = 'Too many \'(\'.'
+                raise ValueError (Msg)
+            self.Depex += self.OpConverter.OpEncode (self._opReference[Operator][1])
+        self.Depex += self.OpConverter.OpEncode (0x0D)
+
+        self._Valid = True
+        self._DepexSize = len (self.Depex)
+        return self.Depex + self.Payload
+
+    def Decode (self, Buffer):
+        # initialize
+        self.Depex = Buffer
+        OperandStack = []
+        DepexLen = 0
+
+        while True:
+            Opcode, Operand, OperandSize = self.OpConverter.OpDecode (Buffer[DepexLen:])
+            DepexLen += OperandSize + 1
+
+            if Opcode == 0x0D:
+                break
+
+            elif Opcode == 0x02:
+                if not OperandStack:
+                    OperandStack.append ('DECLARE \"{String}\"'.format (String = Operand))
+                else:
+                    PrevOperand = OperandStack.pop ()
+                    OperandStack.append ('{Operand} DECLARE 
+ \"{String}\"'.format (Operand = PrevOperand, String = Operand))
+
+            elif Opcode in [0x00, 0x01]:
+                OperandStack.append (Operand)
+
+            elif Opcode == 0x06:
+                OperandStack.append ('TRUE')
+
+            elif Opcode == 0x07:
+                OperandStack.append ('FALSE')
+
+            elif self.IsValidOperator (self._opReferenceReverse[Opcode]):
+                Operator = self._opReferenceReverse[Opcode]
+                if self.IsValidUnaryOperator (self._opReferenceReverse[Opcode]) and len (OperandStack) >= 1:
+                    Oprand = OperandStack.pop ()
+                    OperandStack.append (' ( {Operator} {Oprand} )'.format (Operator = Operator, Oprand = Oprand))
+                elif self.IsValidBinocularOperator (self._opReferenceReverse[Opcode]) and len (OperandStack) >= 2:
+                    Oprand1 = OperandStack.pop ()
+                    Oprand2 = OperandStack.pop ()
+                    OperandStack.append (' ( {Oprand1} {Operator} {Oprand2} )'.format (Operator = Operator, Oprand1 = Oprand1, Oprand2 = Oprand2))
+                else:
+                    Msg = 'No enough Operands for {Opcode:02X}.'.format (Opcode = Opcode)
+                    raise ValueError (Msg)
+
+            else:
+                Msg = '{Opcode:02X} is not a valid OpCode.'.format (Opcode = Opcode)
+                raise ValueError (Msg)
+
+        self.DepexExp = OperandStack[0].strip (' ')
+        self.Payload = Buffer[DepexLen:]
+        self._Valid = True
+        self._DepexSize = DepexLen
+        return self.Payload
+
+
+    def DumpInfo (self):
+        DepexLen = 0
+        Opcode = None
+        Buffer = self.Depex
+
+        if self._Valid == True:
+            print ('EFI_FIRMWARE_IMAGE_DEP.Dependencies = {')
+            while Opcode != 0x0D:
+                Opcode, Operand, OperandSize = self.OpConverter.OpDecode (Buffer[DepexLen:])
+                DepexLen += OperandSize + 1
+                if Operand:
+                    print ('    {Opcode:02X}, {Operand},'.format (Opcode = Opcode, Operand = Operand))
+                else:
+                    print ('    {Opcode:02X},'.format (Opcode = Opcode))
+            print ('}')
+
+            print ('sizeof (EFI_FIRMWARE_IMAGE_DEP.Dependencies)    = {Size:08X}'.format (Size = self._DepexSize))
+            print ('sizeof (Payload)                                = {Size:08X}'.format (Size = len (self.Payload)))
--
2.18.0.windows.1


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.

View/Reply Online (#53253): https://edk2.groups.io/g/devel/message/53253
Mute This Topic: https://groups.io/mt/69594821/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub  [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-