def getTrailer(signature, sigTypeDescrip, descripFixedSize, sigFixedSize): """ create a trailer which has the following format [a + b + c], where a = signature type description (Use fixed size to store it. Use zero to fill up the rest if description size is smaller than fixed size) b = size of signature (converted into hexadecimal representation in 32 bits in little endian format) c = signature (Used fixed size to store it. Use zero to fill up the rest if signature size is smaller than fixed size) :param signature: :param sigTypeDescrip: signature type description :param descripFixedSize: fixed size in bytes for storing signature type description :param sigFixedSize: fixed size in bytes for storing signature :return: """ trailer = bytearray() # append signature type description to fixed sigTypeDescrip = bytearray(source=sigTypeDescrip, encoding="utf-8") typeDescripSize = len(sigTypeDescrip) if typeDescripSize > descripFixedSize: raise Exception( "Signature type description exceeded allowed size! Description size: " + sigTypeDescrip + ". Allowed maximum size: " + descripFixedSize + ". Description given: " + sigTypeDescrip) padSize = descripFixedSize - typeDescripSize pad = bytearray(padSize) trailer.extend(sigTypeDescrip) trailer.extend(pad) # append signature size as a 4-byte filed in little endian format sigSize = len(signature) sigSizeFiled = hex(sigSize) sigSizeFiled = format32BitHexStr(sigSizeFiled) sigSizeFiled = toLitteEndianByte(sigSizeFiled) trailer.extend(sigSizeFiled) # append signature if len(signature) > sigFixedSize: raise Exception( "Signature size exceeded allowed size! signature size: " + sigSize + ". Allowed maximum size: " + sigFixedSize) padSize = sigFixedSize - sigSize pad = bytearray(padSize) trailer.extend(signature) trailer.extend(pad) return trailer
def printOTADescriptorImageStruct(processedImagePath, imageLines, offset): """ output the OTA descriptor of processed image along with some of its contents for additionalLines :param processedImagePath: :param imageLines: number of lines to output when printing the image content :param offset: number of bytes to skip from the beginning :return: """ cuttingLine = "--------" f = open(processedImagePath, "rb") f.seek(offset) try: print(cuttingLine + "Generated OTA descriptor" + cuttingLine) byte = f.read(4) print(format32BitHexStr(hex(int.from_bytes(byte, "little"))), " -> sequence number") byte = f.read(4) print(format32BitHexStr(hex(int.from_bytes(byte, "little"))), " -> start address") byte = f.read(4) print(format32BitHexStr(hex(int.from_bytes(byte, "little"))), " -> end address") byte = f.read(4) print(format32BitHexStr(hex(int.from_bytes(byte, "little"))), " -> execution address") byte = f.read(4) print(format32BitHexStr(hex(int.from_bytes(byte, "little"))), " -> hardware ID") byte = f.read(4) print(format32BitHexStr(hex(int.from_bytes(byte, "little"))), " -> reserved bytes") print(cuttingLine + "Image Content" + cuttingLine) time = imageLines while time > 0: byte = f.read(4) # Every time print 32 bits time -= 1 print(format32BitHexStr(hex(int.from_bytes(byte, "little")))) print("...") print("...") finally: f.close()
def printFactoryImageStruct(processedImagePath, trailerSize, numLinesImageContent, descripFixedSize): """ print structure of factory image :param processedImagePath: path of processed image :param trailerSize: size of trailer in bytes :param numLinesImageContent: number of lines of image content to output :param descripFixedSize: fixed size in bytes for storing signature type description :return: """ cuttingLine = "--------" subCuttingLine = "-----" # print magic code with open(processedImagePath, "rb") as f: print(cuttingLine + subCuttingLine + "Magic Code" + subCuttingLine + cuttingLine) byte = f.read(8) print(byte) # print [ota_descriptor + image content] printOTADescriptorImageStruct(processedImagePath, numLinesImageContent, 8) # print trailer fSize = getFileSize(processedImagePath) f = open(processedImagePath, "rb") f.seek(fSize - trailerSize) print(cuttingLine + cuttingLine + "Trailer" + cuttingLine + cuttingLine) try: print(subCuttingLine + " signature type " + subCuttingLine) byte = f.read(descripFixedSize) print(byte) print(subCuttingLine + " signature size " + subCuttingLine) byte = f.read(4) print(format32BitHexStr(hex(struct.unpack('<I', byte)[0]))) byte = f.read() print(subCuttingLine + " signature " + subCuttingLine) print(byte) finally: f.close()
def getOTADescriptor(userConfigFilePath, inputImagePath, ruleFolderPath, hardwarePlatform): """ parse and validate the parameters defined by user. calculate the end address If all parameters are valid, return them as ota descriptor. Otherwise, raise exeception. :param userConfigFilePath: path of the user config file :param inputImagePath: path of the input image, which will be used to calculate the end address :param ruleFolderPath: path of the folder which contains the validation rule file for the user config :param hardwarePlatform: hardware platform name :return: parameters to be used to generate the OTA descriptor. Each parameter will be formatted as a 10-character string to represent a 32-bit hexadecimal number, where the first two character is a prefix "0x". """ parsedParams = parseConfigFile(userConfigFilePath) # 1. validate the hardware platform is valid and corresponding rule file can be found validHardwarePlatforms = os.listdir(ruleFolderPath) if hardwarePlatform not in validHardwarePlatforms: raise Exception( "Invalid hardware platform! \nExpected hardware platforms: " + str( validHardwarePlatforms) + ". \nFound: " + hardwarePlatform) # 2. Make sure the rule file and the parameters defined in it are valid validationFileLocation = os.path.join(ruleFolderPath, hardwarePlatform) validateFilePath(validationFileLocation) ruleParams = parseConfigFile(validationFileLocation) if len(ruleParams.keys()) != 2: raise Exception("Invalid validation rule file : " + validationFileLocation + "\nExpected 2 parameters in this file. " + "\nFound: " + str(len(ruleParams.keys()))) if "MIN_ADDRESS" not in ruleParams: raise Exception("Error! parameter \"MIN_ADDRESS\" is not defined in " + validationFileLocation) minAddrHardwarePlatform = ruleParams["MIN_ADDRESS"] validate32BitHexParam(minAddrHardwarePlatform, "MIN_ADDRESS", validationFileLocation) minAddrHardwarePlatform = format32BitHexStr(minAddrHardwarePlatform) if "MAX_ADDRESS" not in ruleParams: raise Exception("Error! parameter \"MAX_ADDRESS\" is not defined in " + validationFileLocation) maxAddrHardwarePlatform = ruleParams["MAX_ADDRESS"] validate32BitHexParam(maxAddrHardwarePlatform, "MAX_ADDRESS", validationFileLocation) maxAddrHardwarePlatform = format32BitHexStr(maxAddrHardwarePlatform) if (int(maxAddrHardwarePlatform, 16) <= int(minAddrHardwarePlatform, 16)): raise Exception( "MAX_ADDRESS must be greater than the MIN_ADDRESS !" + " File location " + validationFileLocation) # 3. validate sequence number if "SEQUENCE_NUMBER" not in parsedParams: raise Exception("Error! parameter \"SEQUENCE_NUMBER\" is not defined in " + userConfigFilePath) sequenceNumber = parsedParams["SEQUENCE_NUMBER"] validate32BitUIntParam(sequenceNumber, "SEQUENCE_NUMBER", userConfigFilePath) # convert from string of 32-bit unsigned integer to string of 32-bit hexadecimal sequenceNumber = hex(int(sequenceNumber, 10)) sequenceNumber = format32BitHexStr(sequenceNumber) # 4. validate hardware id if "HARDWARE_ID" not in parsedParams: raise Exception("Error! parameter \"HARDWARE_ID\" is not defined in " + userConfigFilePath) hardwareID = parsedParams["HARDWARE_ID"] hardwareID = formatHardwareID(hardwareID, userConfigFilePath) hardwareID = format32BitHexStr(hardwareID) # 5. validate reserved bytes if "RESERVED_BYTES" not in parsedParams: raise Exception("Error! parameter \"RESERVED_BYTES\" is not defined in " + userConfigFilePath) reserves = parsedParams["RESERVED_BYTES"] validate32BitHexParam(reserves, "RESERVED_BYTES", userConfigFilePath) reserves = format32BitHexStr(reserves) # 6. validate start address if "START_ADDRESS" not in parsedParams: raise Exception("Error! parameter \"START_ADDRESS\" is not defined in " + userConfigFilePath) startAddress = parsedParams["START_ADDRESS"] validate32BitHexParam(startAddress, "START_ADDRESS", userConfigFilePath) startAddress = format32BitHexStr(startAddress) # make sure minAddrHardwarePlatform <= startAddress < maxAddressHardwarePlatform startMin = minAddrHardwarePlatform startMax = format32BitHexStr(hex(int(maxAddrHardwarePlatform, 16) - 1)) validate32BitHexParamRange(startAddress, "START_ADDRESS", startMin, startMax, userConfigFilePath) # 7. calculate and validate the end address fileSize = getFileSize(inputImagePath) endAddress = getEndAddress(fileSize, int(startAddress, 16)) # get end address in decimal format endAddress = hex(endAddress).upper() # convert to hexadecimal format # make sure minAddrHardwarePlatform < endAddress <= maxAddressHardwarePlatform endMin = format32BitHexStr(hex(int(minAddrHardwarePlatform, 16) + 1)) endMax = maxAddrHardwarePlatform if int(endAddress, 16) < int(endMin, 16) or int(endAddress, 16) > int(endMax, 16): raise Exception( "Invalid value of \"END_ADDRESS\"! Expected range : [" + endMin + "," + endMax + "]. Calculated Result : " + endAddress + "\nPossible reasons: START_ADDRESS is too large or image size is too large (END_ADDRESS = START_ADDRESS + 24 bytes + size of input image)" + "\n\nDebug Info: " + "\nImage size: " + str(fileSize) + "\nSTART_ADDRESS: " + startAddress + ". Config file location" + userConfigFilePath) endAddress = format32BitHexStr(endAddress) # 8. validate the execution address if "EXECUTION_ADDRESS" not in parsedParams: raise Exception("Error! parameter \"EXECUTION_ADDRESS\" is not defined in " + userConfigFilePath) executionAddress = parsedParams["EXECUTION_ADDRESS"] validate32BitHexParam(executionAddress, "EXECUTION_ADDRESS", userConfigFilePath) # make sure startAddress <= executionAddress <= endAddress execMin = startAddress execMax = endAddress validate32BitHexParamRange(executionAddress, "EXECUTION_ADDRESS", execMin, execMax, userConfigFilePath) executionAddress = format32BitHexStr(executionAddress) return OTADescriptor(sequenceNumber, startAddress, endAddress, executionAddress, hardwareID, reserves)
def formatHardwareID(hardwareIdStr, fileLocation): """ format the hardware id into hexadecimal representation raise exception if the given hardwareIdStr is not valid. :param hardwareIdStr: a string representing hardware id :param fileLocation: location where the hardware Id is defined :return: formatted hexadecimal string of hardware id exmaples: hardwarID = "0.0.0" return "0x00000000" hardwarID = "255.255.65535" return "0xFFFFFFFF" hardwarID = "171.205.61355" return "0xABCDEFAB" hardwarID ="1.2.772" return "0x01020304" hardwarID = "000001.000000000002.00000000000772" return "0x01020304" hardwarID = "1.1.1" return "0x01010001" hardwarID = "1.1.00000000" return "0x01010000" hardwarID = "0.0.1" return "0x00000001" """ formatErrorMsg = "Invalid Hardware ID format. Expected format: \"a.b.c\" ,where a,b is 8-bit unsigned integer with range [0,255], c is 16-bit unsigned integer with range range[0,65535]" \ + "\nFound: \"" + hardwareIdStr + "\"." \ + "\nFile location: " + fileLocation fields = hardwareIdStr.split(".") if len(fields) != 3: raise Exception(formatErrorMsg) a, b, c = fields try: a = int(a, 10) b = int(b, 10) c = int(c, 10) except Exception: raise Exception(formatErrorMsg) rangeErrorMsg = "Value of hardware ID is not within expected range. \n" \ + "Expected range for \"a.b.c\" is a->[0,255], b->[0,255], c->[0,65535]. Found: " + hardwareIdStr + "\n" \ + "File location: " + fileLocation if a < 0 or a > 255 or b < 0 or b > 255 or c < 0 or c > 65535: raise Exception(rangeErrorMsg) # convert hardware ID into hex representation # remove prefix if there's any a = hex(a).replace("0x", "").replace("0X", "") b = hex(b).replace("0x", "").replace("0X", "") c = hex(c).replace("0x", "").replace("0X", "") # fill zeroes to get the desired fixed length since we are going to concatenate those hex strings a = a.zfill(2) b = b.zfill(2) c = c.zfill(4) hexStr = a + b + c return format32BitHexStr(hexStr)