def unpack(self, buf, offset): """Unpack an element header from an FFFF header buffer Unpacks an element header from an FFFF header buffer at the specified offset. Returns a flag indicating if the unpacked element is an end-of-table marker """ element_hdr = unpack_from("<LLLLL", buf, offset) type_class = element_hdr[0] self.element_type = type_class & 0x000000ff self.element_class = (type_class >> 8) & 0x00ffffff self.element_id = element_hdr[1] self.element_length = element_hdr[2] self.element_location = element_hdr[3] self.element_generation = element_hdr[4] # Get the element data into our tftf_blob if self.element_type != FFFF_ELEMENT_END_OF_ELEMENT_TABLE: # Create a TFTF blob and load the contents from the specified # TFTF file span_start = self.element_location span_end = span_start + self.element_length self.tftf_blob = Tftf(0, None) span_start = self.element_location span_end = span_start + self.element_length self.tftf_blob.load_tftf_from_buffer(buf[span_start:span_end]) return False else: # EOT is always valid self.in_range = True self.aligned = True self.valid_type = True return True
def init(self): """FFFF Element post-constructor initializer Loads the element from TFTF file, setting the element length to that of the file. Returns a success flag if the file was loaded (no file is treated as success). """ success = True # Try to size it from the TFTF file if self.filename and not self.tftf_blob: # Create a TFTF blob and load the contents from the specified # TFTF file self.tftf_blob = Tftf(0, self.filename) success = self.tftf_blob.load_tftf_file(self.filename) if success and self.tftf_blob.is_good(): # element_length must be that of the entire TFTF blob, # not just the TFTF's "load_length" or "expanded_length". self.element_length = self.tftf_blob.tftf_length else: raise ValueError("Bad TFTF file: {0:x}".format(self.filename)) return True
class FfffElement: """Defines the contents of a Flash Format for Firmware (FFFF) element table Each element describes a region of flash memory, its type and the corresponding blob stored there (typically a TFTF "file"). """ def __init__(self, index, buf, buf_size, erase_block_size, element_type, element_class, element_id, element_length, element_location, element_generation, filename=None): """Constructor Note: The optional filename is merely stored here. It is used later in init() """ # Private vars self.filename = filename self.tftf_blob = None self.buf = buf self.buf_size = buf_size self.index = index self.erase_block_size = erase_block_size self.collisions = [] self.duplicates = [] if element_type == FFFF_ELEMENT_END_OF_ELEMENT_TABLE: # EOT is always valid self.in_range = True self.aligned = True self.valid_type = True else: self.in_range = False self.aligned = False self.valid_type = False # Element fields self.element_type = element_type self.element_class = element_class self.element_id = element_id self.element_length = element_length self.element_location = element_location self.element_generation = element_generation def init(self): """FFFF Element post-constructor initializer Loads the element from TFTF file, setting the element length to that of the file. Returns a success flag if the file was loaded (no file is treated as success). """ success = True # Try to size it from the TFTF file if self.filename and not self.tftf_blob: # Create a TFTF blob and load the contents from the specified # TFTF file self.tftf_blob = Tftf(0, self.filename) success = self.tftf_blob.load_tftf_file(self.filename) if success and self.tftf_blob.is_good(): # element_length must be that of the entire TFTF blob, # not just the TFTF's "load_length" or "expanded_length". self.element_length = self.tftf_blob.tftf_length else: raise ValueError("Bad TFTF file: {0:x}".format(self.filename)) return True def unpack(self, buf, offset): """Unpack an element header from an FFFF header buffer Unpacks an element header from an FFFF header buffer at the specified offset. Returns a flag indicating if the unpacked element is an end-of-table marker """ element_hdr = unpack_from("<LLLLL", buf, offset) type_class = element_hdr[0] self.element_type = type_class & 0x000000ff self.element_class = (type_class >> 8) & 0x00ffffff self.element_id = element_hdr[1] self.element_length = element_hdr[2] self.element_location = element_hdr[3] self.element_generation = element_hdr[4] # Get the element data into our tftf_blob if self.element_type != FFFF_ELEMENT_END_OF_ELEMENT_TABLE: # Create a TFTF blob and load the contents from the specified # TFTF file span_start = self.element_location span_end = span_start + self.element_length self.tftf_blob = Tftf(0, None) span_start = self.element_location span_end = span_start + self.element_length self.tftf_blob.load_tftf_from_buffer(buf[span_start:span_end]) return False else: # EOT is always valid self.in_range = True self.aligned = True self.valid_type = True return True def pack(self, buf, offset): """Pack an element header into an FFFF header Packs an element header into into the FFFF header buffer at the specified offset and returns the offset for the next element """ type_class = (self.element_class << 8) | self.element_type pack_into("<LLLLL", buf, offset, type_class, self.element_id, self.element_length, self.element_location, self.element_generation) return offset + FFFF_ELT_LENGTH def validate(self, address_range_low, address_range_high): # Validate an element header # # Returns True if valid, False otherwise # EOT is always valid if self.element_type == FFFF_ELEMENT_END_OF_ELEMENT_TABLE: self.in_range = True self.aligned = True self.valid_type = True return True # Do we overlap the header self.in_range = self.element_location >= address_range_low and \ self.element_location < address_range_high if not self.in_range: error("Element location " + format(self.element_location, "#x") + " falls outside address range " + format(address_range_low, "#x") + "-" + format(address_range_high, "#x")) # check for alignment and type self.aligned = block_aligned(self.element_location, self.erase_block_size) if not self.aligned: error("Element location " + format(self.element_location, "#x") + " unaligned to block size " + format(self.erase_block_size, "#x")) self.valid_type = self.element_type >= \ FFFF_ELEMENT_STAGE2_FIRMWARE_PACKAGE and \ self.element_type <= FFFF_ELEMENT_DATA return self.in_range and self.aligned and self.valid_type def validate_against(self, other): # Validate an element header against another element header # Check for collision start_a = self.element_location end_a = start_a + self.element_length - 1 start_b = other.element_location end_b = start_b + other.element_length - 1 if end_b >= start_a and start_b <= end_a: self.collisions += [other.index] # Check for other duplicate entries per the specification: # "At most, one element table entry with a particular element # type, element ID, and element generation may be present in # the element table." if self.element_type == other.element_type and \ self.element_id == other.element_id and \ self.element_generation == other.element_generation: self.duplicates += [other.index] def same_as(self, other): """Determine if this TFTF is identical to another""" return self.element_type == other.element_type and \ self.element_class == other.element_class and \ self.element_id == other.element_id and \ self.element_length == other.element_length and \ self.element_location == other.element_location and \ self.element_generation == other.element_generation def write(self, filename): """Write an element to a file Write the FFFF element from the FFFF buffer as a binary blob to the specified file. """ # Output the entire FFFF element blob (less padding) with open(filename, 'wb') as wf: wf.write(self.buf[self.element_location: self.element_location + self.element_length]) print("Wrote", filename) def element_name(self, element_type): # Convert an element type into textual form if element_type in element_names: return element_names[element_type] else: return "?" def element_short_name(self, element_type): # Convert an element type into a (short) textual form if element_type in element_short_names: return element_short_names[element_type] def display_table_header(self): # Print the element table column names print(" Type Class ID Length Location Generation") def display(self, expand_type): """Print an element header Print an element header in numeric form, optionally expanding the element type into a textual form """ # Print the element data element_string = " {0:2d}".format(self.index) element_string += " {0:02x} ".format(self.element_type) element_string += " {0:06x}".format(self.element_class) element_string += " {0:08x}".format(self.element_id) element_string += " {0:08x}".format(self.element_length) element_string += " {0:08x}".format(self.element_location) element_string += " {0:08x}".format(self.element_generation) if expand_type: element_string += \ " ({0:s})".format(self.element_name(self.element_type)) print(element_string) # Note any collisions and duplicates on separate lines if len(self.collisions) > 0: element_string = " Collides with element(s):" for collision in self.collisions[self.index]: element_string += " {0:d}".format(collision) print(element_string) if len(self.duplicates) > 0: element_string = " Duplicates element(s):" for duplicate in self.duplicates[self.index]: element_string += " {0:d}".format(duplicate) print(element_string) # Note any other errors element_string = "" if not self.in_range: element_string += "Out-of-range " if not self.aligned: element_string += "Misaligned " if not self.valid_type: element_string += "Invalid-type " if len(element_string) > 0: error(element_string) def display_element_data(self, header_index): """Print the data blob associated with this element Print an element header's TFTF info """ if self.element_type != FFFF_ELEMENT_END_OF_ELEMENT_TABLE: self.tftf_blob.display("element [{0:d}]".format(self.index), " ") self.tftf_blob.display_data("element [{0:d}]". format(self.index), " ") def write_map_payload(self, wf, base_offset, prefix=""): """Display the field names and offsets of a single FFFF header""" elt_name = "{0:s}element[{1:d}].{2:s}".\ format(prefix, self.index, self.element_short_name(self.element_type)) # Dump the element starts if self.tftf_blob: # We've got a TFTF, pass that on to TFTF to display self.tftf_blob.write_map(wf, self.element_location, elt_name) elif self.element_type != FFFF_ELEMENT_END_OF_ELEMENT_TABLE: # Just print the element payload location wf.write("{0:s} {1:08x}\n". format(prefix, self.element_location))
class FfffElement: """Defines the contents of a Flash Format for Firmware (FFFF) element table Each element describes a region of flash memory, its type and the corresponding blob stored there (typically a TFTF "file"). """ def __init__(self, index, buf, buf_size, erase_block_size, element_type, element_class, element_id, element_length, element_location, element_generation, filename=None): """Constructor Note: The optional filename is merely stored here. It is used later in init() """ # Private vars self.filename = filename self.tftf_blob = None self.buf = buf self.buf_size = buf_size self.index = index self.erase_block_size = erase_block_size self.collisions = [] self.duplicates = [] if element_type == FFFF_ELEMENT_END_OF_ELEMENT_TABLE: # EOT is always valid self.in_range = True self.aligned = True self.valid_type = True else: self.in_range = False self.aligned = False self.valid_type = False # Element fields self.element_type = element_type self.element_class = element_class self.element_id = element_id self.element_length = element_length self.element_location = element_location self.element_generation = element_generation def init(self): """FFFF Element post-constructor initializer Loads the element from TFTF file, setting the element length to that of the file. Returns a success flag if the file was loaded (no file is treated as success). """ success = True # Try to size it from the TFTF file if self.filename and not self.tftf_blob: # Create a TFTF blob and load the contents from the specified # TFTF file self.tftf_blob = Tftf(0, self.filename) success = self.tftf_blob.load_tftf_file(self.filename) if success and self.tftf_blob.is_good(): # element_length must be that of the entire TFTF blob, # not just the TFTF's "load_length" or "expanded_length". self.element_length = self.tftf_blob.tftf_length else: raise ValueError("Bad TFTF file: {0:x}".format(self.filename)) return True def unpack(self, buf, offset): """Unpack an element header from an FFFF header buffer Unpacks an element header from an FFFF header buffer at the specified offset. Returns a flag indicating if the unpacked element is an end-of-table marker """ element_hdr = unpack_from("<LLLLL", buf, offset) type_class = element_hdr[0] self.element_type = type_class & 0x000000ff self.element_class = (type_class >> 8) & 0x00ffffff self.element_id = element_hdr[1] self.element_length = element_hdr[2] self.element_location = element_hdr[3] self.element_generation = element_hdr[4] # Get the element data into our tftf_blob if self.element_type != FFFF_ELEMENT_END_OF_ELEMENT_TABLE: # Create a TFTF blob and load the contents from the specified # TFTF file span_start = self.element_location span_end = span_start + self.element_length self.tftf_blob = Tftf(0, None) span_start = self.element_location span_end = span_start + self.element_length self.tftf_blob.load_tftf_from_buffer(buf[span_start:span_end]) return False else: # EOT is always valid self.in_range = True self.aligned = True self.valid_type = True return True def pack(self, buf, offset): """Pack an element header into an FFFF header Packs an element header into into the FFFF header buffer at the specified offset and returns the offset for the next element """ type_class = (self.element_class << 8) | self.element_type pack_into("<LLLLL", buf, offset, type_class, self.element_id, self.element_length, self.element_location, self.element_generation) return offset + FFFF_ELT_LENGTH def validate(self, address_range_low, address_range_high): # Validate an element header # # Returns True if valid, False otherwise # EOT is always valid if self.element_type == FFFF_ELEMENT_END_OF_ELEMENT_TABLE: self.in_range = True self.aligned = True self.valid_type = True return True # Do we overlap the header self.in_range = self.element_location >= address_range_low and \ self.element_location < address_range_high if not self.in_range: error("Element location " + format(self.element_location, "#x") + " falls outside address range " + format(address_range_low, "#x") + "-" + format(address_range_high, "#x")) # check for alignment and type self.aligned = block_aligned(self.element_location, self.erase_block_size) if not self.aligned: error("Element location " + format(self.element_location, "#x") + " unaligned to block size " + format(self.erase_block_size, "#x")) self.valid_type = self.element_type >= \ FFFF_ELEMENT_STAGE2_FIRMWARE_PACKAGE and \ self.element_type <= FFFF_ELEMENT_DATA return self.in_range and self.aligned and self.valid_type def validate_against(self, other): # Validate an element header against another element header # Check for collision start_a = self.element_location end_a = start_a + self.element_length - 1 start_b = other.element_location end_b = start_b + other.element_length - 1 if end_b >= start_a and start_b <= end_a: self.collisions += [other.index] # Check for other duplicate entries per the specification: # "At most, one element table entry with a particular element # type, element ID, and element generation may be present in # the element table." if self.element_type == other.element_type and \ self.element_id == other.element_id and \ self.element_generation == other.element_generation: self.duplicates += [other.index] def same_as(self, other): """Determine if this TFTF is identical to another""" return self.element_type == other.element_type and \ self.element_class == other.element_class and \ self.element_id == other.element_id and \ self.element_length == other.element_length and \ self.element_location == other.element_location and \ self.element_generation == other.element_generation def write(self, filename): """Write an element to a file Write the FFFF element from the FFFF buffer as a binary blob to the specified file. """ # Output the entire FFFF element blob (less padding) with open(filename, 'wb') as wf: wf.write(self.buf[self.element_location:self.element_location + self.element_length]) print("Wrote", filename) def element_name(self, element_type): # Convert an element type into textual form if element_type in element_names: return element_names[element_type] else: return "?" def element_short_name(self, element_type): # Convert an element type into a (short) textual form if element_type in element_short_names: return element_short_names[element_type] def display_table_header(self): # Print the element table column names print(" Type Class ID Length Location Generation") def display(self, expand_type): """Print an element header Print an element header in numeric form, optionally expanding the element type into a textual form """ # Print the element data element_string = " {0:2d}".format(self.index) element_string += " {0:02x} ".format(self.element_type) element_string += " {0:06x}".format(self.element_class) element_string += " {0:08x}".format(self.element_id) element_string += " {0:08x}".format(self.element_length) element_string += " {0:08x}".format(self.element_location) element_string += " {0:08x}".format(self.element_generation) if expand_type: element_string += \ " ({0:s})".format(self.element_name(self.element_type)) print(element_string) # Note any collisions and duplicates on separate lines if len(self.collisions) > 0: element_string = " Collides with element(s):" for collision in self.collisions[self.index]: element_string += " {0:d}".format(collision) print(element_string) if len(self.duplicates) > 0: element_string = " Duplicates element(s):" for duplicate in self.duplicates[self.index]: element_string += " {0:d}".format(duplicate) print(element_string) # Note any other errors element_string = "" if not self.in_range: element_string += "Out-of-range " if not self.aligned: element_string += "Misaligned " if not self.valid_type: element_string += "Invalid-type " if len(element_string) > 0: error(element_string) def display_element_data(self, header_index): """Print the data blob associated with this element Print an element header's TFTF info """ if self.element_type != FFFF_ELEMENT_END_OF_ELEMENT_TABLE: self.tftf_blob.display("element [{0:d}]".format(self.index), " ") self.tftf_blob.display_data("element [{0:d}]".format(self.index), " ") def write_map_payload(self, wf, base_offset, prefix=""): """Display the field names and offsets of a single FFFF header""" elt_name = "{0:s}element[{1:d}].{2:s}".\ format(prefix, self.index, self.element_short_name(self.element_type)) # Dump the element starts if self.tftf_blob: # We've got a TFTF, pass that on to TFTF to display self.tftf_blob.write_map(wf, self.element_location, elt_name) elif self.element_type != FFFF_ELEMENT_END_OF_ELEMENT_TABLE: # Just print the element payload location wf.write("{0:s} {1:08x}\n".format(prefix, self.element_location))