]> git.proxmox.com Git - mirror_edk2.git/blobdiff - BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py
BaseTools/Capsule: Add capsule dependency support
[mirror_edk2.git] / BaseTools / Source / Python / Common / Uefi / Capsule / CapsuleDependency.py
diff --git a/BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py b/BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py
new file mode 100644 (file)
index 0000000..7400485
--- /dev/null
@@ -0,0 +1,409 @@
+## @file\r
+# Module that encodes and decodes a capsule dependency.\r
+#\r
+# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
+#\r
+import struct\r
+import json\r
+import sys\r
+import uuid\r
+import re\r
+\r
+'''\r
+CapsuleDependency\r
+'''\r
+\r
+class OpConvert (object):\r
+    def __init__ (self):\r
+        # Opcode: (OperandSize, PackSize, PackFmt, EncodeConvert, DecodeConvert)\r
+        self._DepexOperations = {0x00:    (16, 16, 's', self.Str2Guid, self.Guid2Str),\r
+                                 0x01:    (4,  1,  'I', self.Str2Uint, self.Uint2Str),\r
+                                 0x02:    (1,  0,  's', self.Str2Utf8, self.Byte2Str),\r
+                                 }\r
+\r
+    def Str2Uint (self, Data):\r
+        try:\r
+            Value = int (Data, 16)\r
+        except:\r
+            Message = '{Data} is not a valid integer value.'.format (Data = Data)\r
+            raise ValueError (Message)\r
+        if Value < 0 or Value > 0xFFFFFFFF:\r
+            Message = '{Data} is not an UINT32.'.format (Data = Data)\r
+            raise ValueError (Message)\r
+        return Value\r
+\r
+    def Uint2Str (self, Data):\r
+        if Data < 0 or Data > 0xFFFFFFFF:\r
+            Message = '{Data} is not an UINT32.'.format (Data = Data)\r
+            raise ValueError (Message)\r
+        return "0x{Data:08x}".format (Data = Data)\r
+\r
+    def Str2Guid (self, Data):\r
+        try:\r
+            Guid = uuid.UUID (Data)\r
+        except:\r
+            Message = '{Data} is not a valid registry format GUID value.'.format (Data = Data)\r
+            raise ValueError (Message)\r
+        return Guid.bytes_le\r
+\r
+    def Guid2Str (self, Data):\r
+        try:\r
+            Guid = uuid.UUID (bytes_le = Data)\r
+        except:\r
+            Message = '{Data} is not a valid binary format GUID value.'.format (Data = Data)\r
+            raise ValueError (Message)\r
+        return str (Guid).upper ()\r
+\r
+    def Str2Utf8 (self, Data):\r
+        if isinstance (Data, str):\r
+            return Data.encode ('utf-8')\r
+        else:\r
+            Message = '{Data} is not a valid string.'.format (Data = Data)\r
+            raise ValueError (Message)\r
+\r
+    def Byte2Str (self, Data):\r
+        if isinstance (Data, bytes):\r
+            if Data[-1:] == b'\x00':\r
+                return str (Data[:-1], 'utf-8')\r
+            else:\r
+                return str (Data, 'utf-8')\r
+        else:\r
+            Message = '{Data} is not a valid binary string.'.format (Data = Data)\r
+            raise ValueError (Message)\r
+\r
+    def OpEncode (self, Opcode, Operand = None):\r
+        BinTemp = struct.pack ('<b', Opcode)\r
+        if Opcode <= 0x02 and Operand != None:\r
+            OperandSize, PackSize, PackFmt, EncodeConvert, DecodeConvert = self._DepexOperations[Opcode]\r
+            Value = EncodeConvert (Operand)\r
+            if Opcode == 0x02:\r
+                PackSize = len (Value) + 1\r
+            BinTemp += struct.pack ('<{PackSize}{PackFmt}'.format (PackSize = PackSize, PackFmt = PackFmt), Value)\r
+        return BinTemp\r
+\r
+    def OpDecode (self, Buffer):\r
+        Opcode = struct.unpack ('<b', Buffer[0:1])[0]\r
+        if Opcode <= 0x02:\r
+            OperandSize, PackSize, PackFmt, EncodeConvert, DecodeConvert = self._DepexOperations[Opcode]\r
+            if Opcode == 0x02:\r
+                try:\r
+                    PackSize = Buffer[1:].index (b'\x00') + 1\r
+                    OperandSize = PackSize\r
+                except:\r
+                    Message = 'CapsuleDependency: OpConvert: error: decode failed with wrong opcode/string.'\r
+                    raise ValueError (Message)\r
+            try:\r
+                Operand = DecodeConvert (struct.unpack ('<{PackSize}{PackFmt}'.format (PackSize = PackSize, PackFmt = PackFmt), Buffer[1:1+OperandSize])[0])\r
+            except:\r
+                Message = 'CapsuleDependency: OpConvert: error: decode failed with unpack failure.'\r
+                raise ValueError (Message)\r
+        else:\r
+            Operand = None\r
+            OperandSize = 0\r
+        return (Opcode, Operand, OperandSize)\r
+\r
+class CapsuleDependencyClass (object):\r
+    # //**************************************************************\r
+    # // Image Attribute - Dependency\r
+    # //**************************************************************\r
+    # typedef struct {\r
+    #   UINT8 Dependencies[];\r
+    # } EFI_FIRMWARE_IMAGE_DEP\r
+\r
+    # {expression operator : [precedence, opcode, type (1:unary/2:binocular)]}\r
+    _opReference = {'&&':  [2, 0x03, 2],\r
+                    '||':  [1, 0x04, 2],\r
+                    '~':   [5, 0x05, 1],\r
+                    '==':  [3, 0x08, 2],\r
+                    '>':   [4, 0x09, 2],\r
+                    '>=':  [4, 0x0A, 2],\r
+                    '<':   [4, 0x0B, 2],\r
+                    '<=':  [4, 0x0C, 2],\r
+                    }\r
+\r
+    def __init__ (self):\r
+        self.Payload              = b''\r
+        self._DepexExp            = None\r
+        self._DepexList           = []\r
+        self._DepexDump           = []\r
+        self.Depex                = b''\r
+        self._Valid               = False\r
+        self._DepexSize           = 0\r
+        self._opReferenceReverse  = {v[1] : k for k, v in self._opReference.items ()}\r
+        self.OpConverter          = OpConvert ()\r
+\r
+    @property\r
+    def DepexExp (self):\r
+        return self._DepexExp\r
+\r
+    @DepexExp.setter\r
+    def DepexExp (self, DepexExp = ''):\r
+        if isinstance (DepexExp, str):\r
+            DepexExp = re.sub (r'\n',r' ',DepexExp)\r
+            DepexExp = re.sub (r'\(',r' ( ',DepexExp)\r
+            DepexExp = re.sub (r'\)',r' ) ',DepexExp)\r
+            DepexExp = re.sub (r'~',r' ~ ',DepexExp)\r
+            self._DepexList = re.findall(r"[^\s\"\']+|\"[^\"]*\"|\'[^\']*\'",DepexExp)\r
+            self._DepexExp  = " ".join(self._DepexList)\r
+\r
+        else:\r
+            Msg = 'Input Depex Expression is not valid string.'\r
+            raise ValueError (Msg)\r
+\r
+    def IsValidOperator (self, op):\r
+        return op in self._opReference.keys ()\r
+\r
+    def IsValidUnaryOperator (self, op):\r
+        return op in self._opReference.keys () and self._opReference[op][2] == 1\r
+\r
+    def IsValidBinocularOperator (self, op):\r
+        return op in self._opReference.keys () and self._opReference[op][2] == 2\r
+\r
+    def IsValidGuid (self, operand):\r
+        try:\r
+            uuid.UUID (operand)\r
+        except:\r
+            return False\r
+        return True\r
+\r
+    def IsValidVersion (self, operand):\r
+        try:\r
+            Value = int (operand, 16)\r
+            if Value < 0 or Value > 0xFFFFFFFF:\r
+                return False\r
+        except:\r
+            return False\r
+        return True\r
+\r
+    def IsValidBoolean (self, operand):\r
+        try:\r
+            return operand.upper () in ['TRUE', 'FALSE']\r
+        except:\r
+            return False\r
+\r
+    def IsValidOperand (self, operand):\r
+        return self.IsValidVersion (operand) or self.IsValidGuid (operand) or self.IsValidBoolean (operand)\r
+\r
+    def IsValidString (self, operand):\r
+        return operand[0] == "\"" and operand[-1] == "\"" and len(operand) >= 2\r
+\r
+    # Check if priority of current operater is greater than pervious op\r
+    def PriorityNotGreater (self, prevOp, currOp):\r
+        return self._opReference[currOp][0] <= self._opReference[prevOp][0]\r
+\r
+    def ValidateDepex (self):\r
+        OpList = self._DepexList\r
+\r
+        i = 0\r
+        while i < len (OpList):\r
+            Op = OpList[i]\r
+\r
+            if Op == 'DECLARE':\r
+                i += 1\r
+                if i >= len (OpList):\r
+                    Msg = 'No more Operand after {Op}.'.format (Op = OpList[i-1])\r
+                    raise IndexError (Msg)\r
+                # Check valid string\r
+                if not self.IsValidString(OpList[i]):\r
+                    Msg = '{Operand} after {Op} is not a valid expression input.'.format (Operand = OpList[i], Op = OpList[i-1])\r
+                    raise ValueError (Msg)\r
+\r
+            elif Op == '(':\r
+                # Expression cannot end with (\r
+                if i == len (OpList) - 1:\r
+                    Msg = 'Expression cannot end with \'(\''\r
+                    raise ValueError (Msg)\r
+                # The previous op after '(' cannot be a binocular operator\r
+                if self.IsValidBinocularOperator (OpList[i+1]) :\r
+                    Msg = '{Op} after \'(\' is not a valid expression input.'.format (Op = OpList[i+1])\r
+                    raise ValueError (Msg)\r
+\r
+            elif Op == ')':\r
+                # Expression cannot start with )\r
+                if i == 0:\r
+                    Msg = 'Expression cannot start with \')\''\r
+                    raise ValueError (Msg)\r
+                # The previous op before ')' cannot be an operator\r
+                if self.IsValidOperator (OpList[i-1]):\r
+                    Msg = '{Op} before \')\' is not a valid expression input.'.format (Op = OpList[i-1])\r
+                    raise ValueError (Msg)\r
+                # The next op after ')' cannot be operand or unary operator\r
+                if (i + 1) < len (OpList) and (self.IsValidOperand (OpList[i+1]) or self.IsValidUnaryOperator (OpList[i+1])):\r
+                    Msg = '{Op} after \')\' is not a valid expression input.'.format (Op = OpList[i+1])\r
+                    raise ValueError (Msg)\r
+\r
+            elif self.IsValidOperand (Op):\r
+                # The next expression of operand cannot be operand or unary operator\r
+                if (i + 1) < len (OpList) and (self.IsValidOperand (OpList[i+1]) or self.IsValidUnaryOperator (OpList[i+1])):\r
+                    Msg = '{Op} after {PrevOp} is not a valid expression input.'.format (Op = OpList[i+1], PrevOp = Op)\r
+                    raise ValueError (Msg)\r
+\r
+            elif self.IsValidOperator (Op):\r
+                # The next op of operator cannot binocular operator\r
+                if (i + 1) < len (OpList) and self.IsValidBinocularOperator (OpList[i+1]):\r
+                    Msg = '{Op} after {PrevOp} is not a valid expression input.'.format (Op = OpList[i+1], PrevOp = Op)\r
+                    raise ValueError (Msg)\r
+                # The first op can not be binocular operator\r
+                if i == 0 and self.IsValidBinocularOperator (Op):\r
+                    Msg = 'Expression cannot start with an operator {Op}.'.format (Op = Op)\r
+                    raise ValueError (Msg)\r
+                # The last op can not be operator\r
+                if i == len (OpList) - 1:\r
+                    Msg = 'Expression cannot ended with an operator {Op}.'.format (Op = Op)\r
+                    raise ValueError (Msg)\r
+                # The next op of unary operator cannot be guid / version\r
+                if self.IsValidUnaryOperator (Op) and (self.IsValidGuid (OpList[i+1]) or self.IsValidVersion (OpList[i+1])):\r
+                    Msg = '{Op} after {PrevOp} is not a valid expression input.'.format (Op = OpList[i+1], PrevOp = Op)\r
+                    raise ValueError (Msg)\r
+\r
+            else:\r
+                Msg = '{Op} is not a valid expression input.'.format (Op = Op)\r
+                raise ValueError (Msg)\r
+            i += 1\r
+\r
+    def Encode (self):\r
+        # initialize\r
+        self.Depex = b''\r
+        self._DepexDump = []\r
+        OperandStack = []\r
+        OpeartorStack = []\r
+        OpList = self._DepexList\r
+\r
+        self.ValidateDepex ()\r
+\r
+        # convert\r
+        i = 0\r
+        while i < len (OpList):\r
+            Op = OpList[i]\r
+            if Op == 'DECLARE':\r
+                # This declare next expression value is a VERSION_STRING\r
+                i += 1\r
+                self.Depex += self.OpConverter.OpEncode (0x02, OpList[i][1:-1])\r
+\r
+            elif Op == '(':\r
+                OpeartorStack.append (Op)\r
+\r
+            elif Op == ')':\r
+                while (OpeartorStack and OpeartorStack[-1] != '('):\r
+                    Operator = OpeartorStack.pop ()\r
+                    self.Depex += self.OpConverter.OpEncode (self._opReference[Operator][1])\r
+                try:\r
+                    OpeartorStack.pop () # pop out '('\r
+                except:\r
+                    Msg = 'Pop out \'(\' failed, too many \')\''\r
+                    raise ValueError (Msg)\r
+\r
+            elif self.IsValidGuid (Op):\r
+                if not OperandStack:\r
+                    OperandStack.append (self.OpConverter.OpEncode (0x00, Op))\r
+                else:\r
+                    # accroding to uefi spec 2.8, the guid/version operands is a reversed order in firmware comparison.\r
+                    self.Depex += self.OpConverter.OpEncode (0x00, Op)\r
+                    self.Depex += OperandStack.pop ()\r
+\r
+            elif self.IsValidVersion (Op):\r
+                if not OperandStack:\r
+                    OperandStack.append (self.OpConverter.OpEncode (0x01, Op))\r
+                else:\r
+                    # accroding to uefi spec 2.8, the guid/version operands is a reversed order in firmware comparison.\r
+                    self.Depex += self.OpConverter.OpEncode (0x01, Op)\r
+                    self.Depex += OperandStack.pop ()\r
+\r
+            elif self.IsValidBoolean (Op):\r
+                if Op.upper () == 'FALSE':\r
+                    self.Depex += self.OpConverter.OpEncode (0x07)\r
+                elif Op.upper () == 'TRUE':\r
+                    self.Depex += self.OpConverter.OpEncode (0x06)\r
+\r
+            elif self.IsValidOperator (Op):\r
+                while (OpeartorStack and OpeartorStack[-1] != '(' and self.PriorityNotGreater (OpeartorStack[-1], Op)):\r
+                    Operator = OpeartorStack.pop ()\r
+                    self.Depex += self.OpConverter.OpEncode (self._opReference[Operator][1])\r
+                OpeartorStack.append (Op)\r
+\r
+            i += 1\r
+\r
+        while OpeartorStack:\r
+            Operator = OpeartorStack.pop ()\r
+            if Operator == '(':\r
+                Msg = 'Too many \'(\'.'\r
+                raise ValueError (Msg)\r
+            self.Depex += self.OpConverter.OpEncode (self._opReference[Operator][1])\r
+        self.Depex += self.OpConverter.OpEncode (0x0D)\r
+\r
+        self._Valid = True\r
+        self._DepexSize = len (self.Depex)\r
+        return self.Depex + self.Payload\r
+\r
+    def Decode (self, Buffer):\r
+        # initialize\r
+        self.Depex = Buffer\r
+        OperandStack = []\r
+        DepexLen = 0\r
+\r
+        while True:\r
+            Opcode, Operand, OperandSize = self.OpConverter.OpDecode (Buffer[DepexLen:])\r
+            DepexLen += OperandSize + 1\r
+\r
+            if Opcode == 0x0D:\r
+                break\r
+\r
+            elif Opcode == 0x02:\r
+                if not OperandStack:\r
+                    OperandStack.append ('DECLARE \"{String}\"'.format (String = Operand))\r
+                else:\r
+                    PrevOperand = OperandStack.pop ()\r
+                    OperandStack.append ('{Operand} DECLARE \"{String}\"'.format (Operand = PrevOperand, String = Operand))\r
+\r
+            elif Opcode in [0x00, 0x01]:\r
+                OperandStack.append (Operand)\r
+\r
+            elif Opcode == 0x06:\r
+                OperandStack.append ('TRUE')\r
+\r
+            elif Opcode == 0x07:\r
+                OperandStack.append ('FALSE')\r
+\r
+            elif self.IsValidOperator (self._opReferenceReverse[Opcode]):\r
+                Operator = self._opReferenceReverse[Opcode]\r
+                if self.IsValidUnaryOperator (self._opReferenceReverse[Opcode]) and len (OperandStack) >= 1:\r
+                    Oprand = OperandStack.pop ()\r
+                    OperandStack.append (' ( {Operator} {Oprand} )'.format (Operator = Operator, Oprand = Oprand))\r
+                elif self.IsValidBinocularOperator (self._opReferenceReverse[Opcode]) and len (OperandStack) >= 2:\r
+                    Oprand1 = OperandStack.pop ()\r
+                    Oprand2 = OperandStack.pop ()\r
+                    OperandStack.append (' ( {Oprand1} {Operator} {Oprand2} )'.format (Operator = Operator, Oprand1 = Oprand1, Oprand2 = Oprand2))\r
+                else:\r
+                    Msg = 'No enough Operands for {Opcode:02X}.'.format (Opcode = Opcode)\r
+                    raise ValueError (Msg)\r
+\r
+            else:\r
+                Msg = '{Opcode:02X} is not a valid OpCode.'.format (Opcode = Opcode)\r
+                raise ValueError (Msg)\r
+\r
+        self.DepexExp = OperandStack[0].strip (' ')\r
+        self.Payload = Buffer[DepexLen:]\r
+        self._Valid = True\r
+        self._DepexSize = DepexLen\r
+        return self.Payload\r
+\r
+\r
+    def DumpInfo (self):\r
+        DepexLen = 0\r
+        Opcode = None\r
+        Buffer = self.Depex\r
+\r
+        if self._Valid == True:\r
+            print ('EFI_FIRMWARE_IMAGE_DEP.Dependencies = {')\r
+            while Opcode != 0x0D:\r
+                Opcode, Operand, OperandSize = self.OpConverter.OpDecode (Buffer[DepexLen:])\r
+                DepexLen += OperandSize + 1\r
+                if Operand:\r
+                    print ('    {Opcode:02X}, {Operand},'.format (Opcode = Opcode, Operand = Operand))\r
+                else:\r
+                    print ('    {Opcode:02X},'.format (Opcode = Opcode))\r
+            print ('}')\r
+\r
+            print ('sizeof (EFI_FIRMWARE_IMAGE_DEP.Dependencies)    = {Size:08X}'.format (Size = self._DepexSize))\r
+            print ('sizeof (Payload)                                = {Size:08X}'.format (Size = len (self.Payload)))\r