diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py')
-rwxr-xr-x | src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py b/src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/Common/Uefi/Capsule/CapsuleDependency.py new file mode 100755 index 00000000..96cf4b40 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/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))) |