--- /dev/null
+## @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