def check_wr_protection_area(self, num_blk, wr_data): # checks fields which have the write protection bit. # if the write protection bit is set, we need to protect that area from changes. write_disable_bit = self.read_field("WR_DIS", bitstring=False) mask_wr_data = BitString(len(wr_data)) mask_wr_data.set(0) blk = self.Blocks.get(self.Blocks.BLOCKS[num_blk]) if blk.write_disable_bit is not None and write_disable_bit & ( 1 << blk.write_disable_bit ): mask_wr_data.set(1) else: for e in self.Fields.EFUSES: field = self.Fields.get(e) if blk.id == field.block and field.block == num_blk: if field.write_disable_bit is not None and write_disable_bit & ( 1 << field.write_disable_bit ): data = self.read_field(field.name) data.set(1) mask_wr_data.pos = mask_wr_data.length - ( field.word * 32 + field.pos + data.len ) mask_wr_data.overwrite(data) mask_wr_data.invert() return wr_data & mask_wr_data
def burn_bit(esp, efuses, args): num_block = efuses.get_index_block_by_name(args.block) block = efuses.blocks[num_block] data_block = BitString(block.get_block_len() * 8) data_block.set(0) try: data_block.set(True, args.bit_number) except IndexError: raise esptool.FatalError("%s has bit_number in [0..%d]" % (args.block, data_block.len - 1)) data_block.reverse() print("bit_number: [%-03d]........................................................[0]" % (data_block.len - 1)) print("BLOCK%-2d :" % block.id, data_block) block.print_block(data_block, "regs_to_write", debug=True) block.save(data_block.bytes[::-1]) efuses.burn_all() print("Successful")
class EfuseFieldBase(EfuseProtectBase): def __init__(self, parent, param): self.category = param.category self.parent = parent self.block = param.block self.word = param.word self.pos = param.pos self.write_disable_bit = param.write_disable_bit self.read_disable_bit = param.read_disable_bit self.name = param.name self.efuse_class = param.class_type self.efuse_type = param.type self.description = param.description self.dict_value = param.dictionary if self.efuse_type.startswith("bool"): field_len = 1 else: field_len = int(re.search(r'\d+', self.efuse_type).group()) if self.efuse_type.startswith("bytes"): field_len *= 8 self.bitarray = BitString(field_len) self.bit_len = field_len self.bitarray.set(0) self.update(self.parent.blocks[self.block].bitarray) def check_format(self, new_value_str): if new_value_str is None: return new_value_str else: if self.efuse_type.startswith("bytes"): if new_value_str.startswith("0x"): # cmd line: 0x0102030405060708 .... 112233ff (hex) # regs: 112233ff ... 05060708 01020304 # BLK: ff 33 22 11 ... 08 07 06 05 04 03 02 01 return binascii.unhexlify(new_value_str[2:])[::-1] else: # cmd line: 0102030405060708 .... 112233ff (string) # regs: 04030201 08070605 ... ff332211 # BLK: 01 02 03 04 05 06 07 08 ... 11 22 33 ff return binascii.unhexlify(new_value_str) else: return new_value_str def convert_to_bitstring(self, new_value): if isinstance(new_value, BitArray): return new_value else: if self.efuse_type.startswith("bytes"): # new_value (bytes) = [0][1][2] ... [N] (original data) # in string format = [0] [1] [2] ... [N] (util.hexify(data, " ")) # in hex format = 0x[N]....[2][1][0] (from bitstring print(data)) # in reg format = [3][2][1][0] ... [N][][][] (as it will be in the device) # in bitstring = [N] ... [2][1][0] (to get a correct bitstring need to reverse new_value) # *[x] - means a byte. return BitArray(bytes=new_value[::-1], length=len(new_value) * 8) else: return BitArray(self.efuse_type + "={}".format(new_value)) def check_new_value(self, bitarray_new_value): bitarray_old_value = self.get_bitstring() if bitarray_new_value.len != bitarray_old_value.len: raise esptool.FatalError( "For {} efuse, the length of the new value is wrong, expected {} bits, was {} bits." .format(self.name, bitarray_old_value.len, bitarray_new_value.len)) if bitarray_new_value == bitarray_old_value: error_msg = "\tThe same value for {} is already burned. Do not change the efuse.".format( self.name) print(error_msg) bitarray_new_value.set(0) else: if self.name not in ["WR_DIS", "RD_DIS"]: # WR_DIS, RD_DIS fields can have already set bits. # Do not neeed to check below condition for them. if bitarray_new_value | bitarray_old_value != bitarray_new_value: error_msg = "\tNew value contains some bits that cannot be cleared (value will be {})".format( bitarray_old_value | bitarray_new_value) self.parent.print_error_msg(error_msg) self.check_wr_rd_protect() def save_to_block(self, bitarray_field): block = self.parent.blocks[self.block] wr_bitarray_temp = block.wr_bitarray.copy() position = wr_bitarray_temp.length - (self.word * 32 + self.pos + bitarray_field.len) wr_bitarray_temp.overwrite(bitarray_field, pos=position) block.wr_bitarray |= wr_bitarray_temp def save(self, new_value): bitarray_field = self.convert_to_bitstring(new_value) self.check_new_value(bitarray_field) self.save_to_block(bitarray_field) def update(self, bit_array_block): field_len = self.bitarray.len bit_array_block.pos = bit_array_block.length - (self.word * 32 + self.pos + field_len) self.bitarray.overwrite(bit_array_block.read(field_len), pos=0) def get_raw(self, from_read=True): """ Return the raw (unformatted) numeric value of the efuse bits Returns a simple integer or (for some subclasses) a bitstring. type: int or bool -> int type: bytes -> bytearray """ return self.get_bitstring(from_read).read(self.efuse_type) def get(self, from_read=True): """ Get a formatted version of the efuse value, suitable for display type: int or bool -> int type: bytes -> string "01 02 03 04 05 06 07 08 ... ". Byte order [0] ... [N]. dump regs: 0x04030201 0x08070605 ... """ if self.efuse_type.startswith("bytes"): return util.hexify(self.get_bitstring(from_read).bytes[::-1], " ") else: return self.get_raw(from_read) def get_meaning(self, from_read=True): """ Get the meaning of efuse from dict if possible, suitable for display """ if self.dict_value: try: return self.dict_value[self.get_raw(from_read)] except KeyError: pass return self.get(from_read) def get_bitstring(self, from_read=True): if from_read: self.bitarray.pos = 0 return self.bitarray else: field_len = self.bitarray.len block = self.parent.blocks[self.block] block.wr_bitarray.pos = block.wr_bitarray.length - ( self.word * 32 + self.pos + field_len) return block.wr_bitarray.read(self.bitarray.len) def burn(self, new_value): # Burn a efuse. Added for compatibility reason. self.save(new_value) self.parent.burn_all()
class EfuseBlockBase(EfuseProtectBase): def __init__(self, parent, param, skip_read=False): self.parent = parent self.name = param.name self.alias = param.alias self.id = param.id self.rd_addr = param.rd_addr self.wr_addr = param.wr_addr self.write_disable_bit = param.write_disable_bit self.read_disable_bit = param.read_disable_bit self.len = param.len self.key_purpose_name = param.key_purpose bit_block_len = self.get_block_len() * 8 self.bitarray = BitString(bit_block_len) self.bitarray.set(0) self.wr_bitarray = BitString(bit_block_len) self.wr_bitarray.set(0) if not skip_read: self.read() def get_block_len(self): coding_scheme = self.get_coding_scheme() if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE: return self.len * 4 elif coding_scheme == self.parent.REGS.CODING_SCHEME_34: return (self.len * 3 // 4) * 4 elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS: return self.len * 4 else: raise esptool.FatalError("Coding scheme (%d) not supported" % (coding_scheme)) def get_coding_scheme(self): if self.id == 0: return self.parent.REGS.CODING_SCHEME_NONE else: return self.parent.coding_scheme def get_raw(self, from_read=True): if from_read: return self.bitarray.bytes else: return self.wr_bitarray.bytes def get(self, from_read=True): self.get_bitstring(from_read=from_read) def get_bitstring(self, from_read=True): if from_read: return self.bitarray else: return self.wr_bitarray def convert_to_bitstring(self, new_data): if isinstance(new_data, BitArray): return new_data else: return BitArray(bytes=new_data, length=len(new_data) * 8) def get_words(self): def get_offsets(self): return [ x + self.rd_addr for x in range(0, self.get_block_len(), 4) ] return [self.parent.read_reg(offs) for offs in get_offsets(self)] def read(self): words = self.get_words() data = BitArray() for word in reversed(words): data.append("uint:32=%d" % word) self.bitarray.overwrite(data, pos=0) self.print_block(self.bitarray, "read_regs") def print_block(self, bit_string, comment, debug=False): if self.parent.debug or debug: bit_string.pos = 0 print( "%-15s (%-16s) [%-2d] %s:" % (self.name, " ".join(self.alias)[:16], self.id, comment), " ".join([ "%08x" % word for word in bit_string.readlist('%d*uint:32' % (bit_string.len / 32))[::-1] ])) def check_wr_data(self): wr_data = self.wr_bitarray if wr_data.all(False): # nothing to burn if self.parent.debug: print("[{:02}] {:20} nothing to burn".format( self.id, self.name)) return False if len(wr_data.bytes) != len(self.bitarray.bytes): raise esptool.FatalError( "Data does not fit: the block%d size is %d bytes, data is %d bytes" % (self.id, len(self.bitarray.bytes), len(wr_data.bytes))) self.check_wr_rd_protect() if self.get_bitstring().all(False): print("[{:02}] {:20} is empty, will burn the new value".format( self.id, self.name)) else: # the written block in chip is not empty if self.get_bitstring() == wr_data: print( "[{:02}] {:20} is already written the same value, continue with EMPTY_BLOCK" .format(self.id, self.name)) wr_data.set(0) else: print("[{:02}] {:20} is not empty".format(self.id, self.name)) print("\t(written ):", self.get_bitstring()) print("\t(to write):", wr_data) mask = self.get_bitstring() & wr_data if mask == wr_data: print( "\tAll wr_data bits are set in the written block, continue with EMPTY_BLOCK." ) wr_data.set(0) else: coding_scheme = self.get_coding_scheme() if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE: print("\t(coding scheme = NONE)") elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS: print("\t(coding scheme = RS)") error_msg = "\tBurn into %s is forbidden (RS coding scheme does not allow this)." % ( self.name) self.parent.print_error_msg(error_msg) elif coding_scheme == self.parent.REGS.CODING_SCHEME_34: print("\t(coding scheme = 3/4)") data_can_not_be_burn = False for i in range(0, self.get_bitstring().len, 6 * 8): rd_chunk = self.get_bitstring()[i:i + 6 * 8:] wr_chunk = wr_data[i:i + 6 * 8:] if rd_chunk.any(True): if wr_chunk.any(True): print( "\twritten chunk [%d] and wr_chunk are not empty. " % (i // (6 * 8)), end="") if rd_chunk == wr_chunk: print( "wr_chunk == rd_chunk. Countinue with empty chunk." ) wr_data[i:i + 6 * 8:].set(0) else: print( "wr_chunk != rd_chunk. Can not burn." ) print("\twritten ", rd_chunk) print("\tto write", wr_chunk) data_can_not_be_burn = True if data_can_not_be_burn: error_msg = "\tBurn into %s is forbidden (3/4 coding scheme does not allow this)." % ( self.name) self.parent.print_error_msg(error_msg) else: raise esptool.FatalError( "The coding scheme ({}) is not supported".format( coding_scheme)) def save(self, new_data): # new_data will be checked by check_wr_data() during burn_all() # new_data (bytes) = [0][1][2] ... [N] (original data) # in string format = [0] [1] [2] ... [N] (util.hexify(data, " ")) # in hex format = 0x[N]....[2][1][0] (from bitstring print(data)) # in reg format = [3][2][1][0] ... [N][][][] (as it will be in the device) # in bitstring = [N] ... [2][1][0] (to get a correct bitstring need to reverse new_data) # *[x] - means a byte. data = BitString(bytes=new_data[::-1], length=len(new_data) * 8) if self.parent.debug: print("\twritten : {} ->\n\tto write: {}".format( self.get_bitstring(), data)) self.wr_bitarray.overwrite(data, pos=0) def burn_words(self, words): self.parent.efuse_controller_setup() if self.parent.debug: print("Write data to BLOCK%d" % (self.id)) write_reg_addr = self.wr_addr for word in words: # for ep32s2: using EFUSE_PGM_DATA[0..7]_REG for writing data # 32 bytes to EFUSE_PGM_DATA[0..7]_REG # 12 bytes to EFUSE_CHECK_VALUE[0..2]_REG. These regs are next after EFUSE_PGM_DATA_REG # for esp32: # each block has the special regs EFUSE_BLK[0..3]_WDATA[0..7]_REG for writing data if self.parent.debug: print("Addr 0x%08x, data=0x%08x" % (write_reg_addr, word)) self.parent.write_reg(write_reg_addr, word) write_reg_addr += 4 warnings_before = self.parent.get_coding_scheme_warnings() warnings_after = self.parent.write_efuses(self.id) if warnings_after & ~warnings_before != 0: print( "WARNING: Burning efuse block added coding scheme warnings 0x%x -> 0x%x. Encoding bug?" % (warnings_before, warnings_after)) def burn(self): if self.wr_bitarray.all(False): # nothing to burn return before_burn_bitarray = self.bitarray[:] assert before_burn_bitarray is not self.bitarray self.print_block(self.wr_bitarray, "to_write") words = self.apply_coding_scheme() self.burn_words(words) self.read() if not self.is_readable(): print( "{} ({}) is read-protected. Read back the burn value is not possible." .format(self.name, self.alias)) if self.bitarray.all(False): print("Read all '0'") else: # Should never be happened raise esptool.FatalError( "The {} is read-protected but not all '0' ({})".format( self.name, self.bitarray.hex)) else: if self.wr_bitarray == self.bitarray: print("BURN BLOCK%-2d - OK (write block == read block)" % self.id) elif self.wr_bitarray & self.bitarray == self.wr_bitarray and self.bitarray & before_burn_bitarray == before_burn_bitarray: print("BURN BLOCK%-2d - OK (all write block bits are set)" % self.id) else: self.print_block(self.wr_bitarray, "Expected") self.print_block(self.bitarray, "Real ") raise esptool.FatalError( "Burn {} ({}) was not successful".format( self.name, self.alias)) self.wr_bitarray.set(0)
class EmulateEfuseControllerBase(object): """ The class for virtual efuse operations. Using for HOST_TEST. """ CHIP_NAME = "" mem = None debug = False Blocks = None Fields = None REGS = None def __init__(self, efuse_file=None, debug=False): self.debug = debug self.efuse_file = efuse_file if self.efuse_file: try: self.mem = BitString(open(self.efuse_file, 'a+b'), length=(self.REGS.EFUSE_MEM_SIZE & self.REGS.EFUSE_ADDR_MASK) * 8) except ValueError: # the file is empty or does not fit the length. self.mem = BitString(length=(self.REGS.EFUSE_MEM_SIZE & self.REGS.EFUSE_ADDR_MASK) * 8) self.mem.set(0) self.mem.tofile(open(self.efuse_file, 'a+b')) else: # efuse_file is not provided it means we do not want to keep the result of efuse operations self.mem = BitString( (self.REGS.EFUSE_MEM_SIZE & self.REGS.EFUSE_ADDR_MASK) * 8) self.mem.set(0) """ esptool method start >> """ def read_efuse(self, n, block=0): """ Read the nth word of the ESP3x EFUSE region. """ blk = self.Blocks.get(self.Blocks.BLOCKS[block]) return self.read_reg(blk.rd_addr + (4 * n)) def read_reg(self, addr): self.mem.pos = self.mem.length - ( (addr & self.REGS.EFUSE_ADDR_MASK) * 8 + 32) return self.mem.read("uint:32") def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0): self.mem.pos = self.mem.length - ( (addr & self.REGS.EFUSE_ADDR_MASK) * 8 + 32) self.mem.overwrite("uint:32={}".format(value & mask)) self.handle_writing_event(addr, value) def update_reg(self, addr, mask, new_val): position = self.mem.length - ( (addr & self.REGS.EFUSE_ADDR_MASK) * 8 + 32) self.mem.pos = position cur_val = self.mem.read("uint:32") self.mem.pos = position self.mem.overwrite("uint:32={}".format(cur_val | (new_val & mask))) def write_efuse(self, n, value, block=0): """ Write the nth word of the ESP3x EFUSE region. """ blk = self.Blocks.get(self.Blocks.BLOCKS[block]) self.write_reg(blk.wr_addr + (4 * n), value) """ << esptool method end """ def handle_writing_event(self, addr, value): self.save_to_file() def save_to_file(self): if self.efuse_file: with open(self.efuse_file, 'wb') as f: self.mem.tofile(f) def handle_coding_scheme(self, blk, data): return data def copy_blocks_wr_regs_to_rd_regs(self, updated_block=None): for b in reversed(self.Blocks.BLOCKS): blk = self.Blocks.get(b) if updated_block is not None: if blk.id != updated_block: continue data = self.read_block(blk.id, wr_regs=True) if self.debug: print(blk.name, data.hex) plain_data = self.handle_coding_scheme(blk, data) plain_data = self.check_wr_protection_area(blk.id, plain_data) self.update_block(blk, plain_data) def clean_blocks_wr_regs(self): for b in self.Blocks.BLOCKS: blk = self.Blocks.get(b) for offset in range(0, blk.len * 4, 4): wr_addr = blk.wr_addr + offset self.write_reg(wr_addr, 0) def read_field(self, name, bitstring=True): for e in self.Fields.EFUSES: field = self.Fields.get(e) if field.name == name: self.read_block(field.block) block = self.read_block(field.block) if field.type.startswith("bool"): field_len = 1 else: field_len = int(re.search(r'\d+', field.type).group()) if field.type.startswith("bytes"): field_len *= 8 block.pos = block.length - (field.word * 32 + field.pos + field_len) if bitstring: return block.read(field_len) else: return block.read(field.type) return None def get_bitlen_of_block(self, blk, wr=False): return 32 * blk.len def read_block(self, idx, wr_regs=False): block = None for b in self.Blocks.BLOCKS: blk = self.Blocks.get(b) if blk.id == idx: blk_len_bits = self.get_bitlen_of_block(blk, wr=wr_regs) addr = blk.wr_addr if wr_regs else blk.rd_addr self.mem.pos = self.mem.length - ( (addr & self.REGS.EFUSE_ADDR_MASK) * 8 + blk_len_bits) block = self.mem.read(blk_len_bits) break return block def update_block(self, blk, wr_data): wr_data = self.read_block(blk.id) | wr_data self.overwrite_mem_from_block(blk, wr_data) def overwrite_mem_from_block(self, blk, wr_data): self.mem.pos = self.mem.length - ( (blk.rd_addr & self.REGS.EFUSE_ADDR_MASK) * 8 + wr_data.len) self.mem.overwrite(wr_data) def check_wr_protection_area(self, num_blk, wr_data): # checks fields which have the write protection bit. # if the write protection bit is set then we need to protect that area from changes. write_disable_bit = self.read_field("WR_DIS", bitstring=False) mask_wr_data = BitString(len(wr_data)) mask_wr_data.set(0) blk = self.Blocks.get(self.Blocks.BLOCKS[num_blk]) if blk.write_disable_bit is not None and write_disable_bit & ( 1 << blk.write_disable_bit): mask_wr_data.set(1) else: for e in self.Fields.EFUSES: field = self.Fields.get(e) if blk.id == field.block and field.block == num_blk: if field.write_disable_bit is not None and write_disable_bit & ( 1 << field.write_disable_bit): data = self.read_field(field.name) data.set(1) mask_wr_data.pos = mask_wr_data.length - ( field.word * 32 + field.pos + data.len) mask_wr_data.overwrite(data) mask_wr_data.invert() return wr_data & mask_wr_data def check_rd_protection_area(self): # checks fields which have the read protection bits. # if the read protection bit is set then we need to reset this field to 0. read_disable_bit = self.read_field("RD_DIS", bitstring=False) for b in self.Blocks.BLOCKS: blk = self.Blocks.get(b) block = self.read_block(blk.id) if blk.read_disable_bit is not None and read_disable_bit & ( 1 << blk.read_disable_bit): block.set(0) else: for e in self.Fields.EFUSES: field = self.Fields.get(e) if blk.id == field.block and field.read_disable_bit is not None and read_disable_bit & ( 1 << field.read_disable_bit): raw_data = self.read_field(field.name) raw_data.set(0) block.pos = block.length - ( field.word * 32 + field.pos + raw_data.length) block.overwrite(BitString(raw_data.length)) self.overwrite_mem_from_block(blk, block) def clean_mem(self): self.mem.set(0) if self.efuse_file: with open(self.efuse_file, 'wb') as f: self.mem.tofile(f)