1# This file describes the common eFuses structures for chips 2# 3# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD 4# 5# SPDX-License-Identifier: GPL-2.0-or-later 6 7import binascii 8import sys 9 10from bitstring import BitArray, BitStream, CreationError 11 12import esptool 13 14from . import util 15from typing import List 16 17 18class CheckArgValue(object): 19 def __init__(self, efuses, name): 20 self.efuses = efuses 21 self.name = name 22 23 def __call__(self, new_value_str): 24 def check_arg_value(efuse, new_value): 25 if efuse.efuse_type.startswith("bool"): 26 new_value = 1 if new_value is None else int(new_value, 0) 27 if new_value != 1: 28 raise esptool.FatalError( 29 "New value is not accepted for efuse '{}' " 30 "(will always burn 0->1), given value={}".format( 31 efuse.name, new_value 32 ) 33 ) 34 elif efuse.efuse_type.startswith(("int", "uint")): 35 if efuse.efuse_class == "bitcount": 36 if new_value is None: 37 # find the first unset bit and set it 38 old_value = efuse.get_raw() 39 new_value = old_value 40 bit = 1 41 while new_value == old_value: 42 new_value = bit | old_value 43 bit <<= 1 44 else: 45 new_value = int(new_value, 0) 46 else: 47 if new_value is None: 48 raise esptool.FatalError( 49 "New value required for efuse '{}' (given None)".format( 50 efuse.name 51 ) 52 ) 53 new_value = int(new_value, 0) 54 if new_value == 0: 55 raise esptool.FatalError( 56 "New value should not be 0 for '{}' " 57 "(given value= {})".format(efuse.name, new_value) 58 ) 59 elif efuse.efuse_type.startswith("bytes"): 60 if new_value is None: 61 raise esptool.FatalError( 62 "New value required for efuse '{}' (given None)".format( 63 efuse.name 64 ) 65 ) 66 if len(new_value) * 8 != efuse.bitarray.len: 67 raise esptool.FatalError( 68 "The length of efuse '{}' ({} bits) " 69 "(given len of the new value= {} bits)".format( 70 efuse.name, efuse.bitarray.len, len(new_value) * 8 71 ) 72 ) 73 else: 74 raise esptool.FatalError( 75 "The '{}' type for the '{}' efuse is not supported yet.".format( 76 efuse.efuse_type, efuse.name 77 ) 78 ) 79 return new_value 80 81 efuse = self.efuses[self.name] 82 new_value = efuse.check_format(new_value_str) 83 return check_arg_value(efuse, new_value) 84 85 86class EfuseProtectBase(object): 87 # This class is used by EfuseBlockBase and EfuseFieldBase 88 89 def get_read_disable_mask(self, blk_part=None): 90 """Returns mask of read protection bits 91 blk_part: 92 - None: Calculate mask for all read protection bits. 93 - a number: Calculate mask only for specific item in read protection list. 94 """ 95 mask = 0 96 if isinstance(self.read_disable_bit, list): 97 if blk_part is None: 98 for i in self.read_disable_bit: 99 mask |= 1 << i 100 else: 101 mask |= 1 << self.read_disable_bit[blk_part] 102 else: 103 mask = 1 << self.read_disable_bit 104 return mask 105 106 def get_count_read_disable_bits(self): 107 """Returns the number of read protection bits used by the field""" 108 # On the C2 chip, BLOCK_KEY0 has two read protection bits [0, 1]. 109 return bin(self.get_read_disable_mask()).count("1") 110 111 def is_readable(self, blk_part=None): 112 """Return true if the efuse is readable by software""" 113 num_bit = self.read_disable_bit 114 if num_bit is None: 115 return True # read cannot be disabled 116 return (self.parent["RD_DIS"].get() & self.get_read_disable_mask(blk_part)) == 0 117 118 def disable_read(self): 119 num_bit = self.read_disable_bit 120 if num_bit is None: 121 raise esptool.FatalError("This efuse cannot be read-disabled") 122 if not self.parent["RD_DIS"].is_writeable(): 123 raise esptool.FatalError( 124 "This efuse cannot be read-disabled due the to RD_DIS field is " 125 "already write-disabled" 126 ) 127 self.parent["RD_DIS"].save(self.get_read_disable_mask()) 128 129 def is_writeable(self): 130 num_bit = self.write_disable_bit 131 if num_bit is None: 132 return True # write cannot be disabled 133 return (self.parent["WR_DIS"].get() & (1 << num_bit)) == 0 134 135 def disable_write(self): 136 num_bit = self.write_disable_bit 137 if not self.parent["WR_DIS"].is_writeable(): 138 raise esptool.FatalError( 139 "This efuse cannot be write-disabled due to the WR_DIS field is " 140 "already write-disabled" 141 ) 142 self.parent["WR_DIS"].save(1 << num_bit) 143 144 def check_wr_rd_protect(self): 145 if not self.is_readable(): 146 error_msg = "\t{} is read-protected.".format(self.name) 147 "The written value can not be read, the efuse/block looks as all 0.\n" 148 error_msg += "\tBurn in this case may damage an already written value." 149 self.parent.print_error_msg(error_msg) 150 if not self.is_writeable(): 151 error_msg = "\t{} is write-protected. Burn is not possible.".format( 152 self.name 153 ) 154 self.parent.print_error_msg(error_msg) 155 156 157class EfuseBlockBase(EfuseProtectBase): 158 def __init__(self, parent, param, skip_read=False): 159 self.parent = parent 160 self.name = param.name 161 self.alias = param.alias 162 self.id = param.id 163 self.rd_addr = param.rd_addr 164 self.wr_addr = param.wr_addr 165 self.write_disable_bit = param.write_disable_bit 166 self.read_disable_bit = param.read_disable_bit 167 self.len = param.len 168 self.key_purpose_name = param.key_purpose 169 bit_block_len = self.get_block_len() * 8 170 self.bitarray = BitStream(bit_block_len) 171 self.bitarray.set(0) 172 self.wr_bitarray = BitStream(bit_block_len) 173 self.wr_bitarray.set(0) 174 self.fail = False 175 self.num_errors = 0 176 if self.id == 0: 177 self.err_bitarray = BitStream(bit_block_len) 178 self.err_bitarray.set(0) 179 else: 180 self.err_bitarray = None 181 182 if not skip_read: 183 self.read() 184 185 def get_block_len(self): 186 coding_scheme = self.get_coding_scheme() 187 if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE: 188 return self.len * 4 189 elif coding_scheme == self.parent.REGS.CODING_SCHEME_34: 190 return (self.len * 3 // 4) * 4 191 elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS: 192 return self.len * 4 193 else: 194 raise esptool.FatalError( 195 "Coding scheme (%d) not supported" % (coding_scheme) 196 ) 197 198 def get_coding_scheme(self): 199 if self.id == 0: 200 return self.parent.REGS.CODING_SCHEME_NONE 201 else: 202 return self.parent.coding_scheme 203 204 def get_raw(self, from_read=True): 205 if from_read: 206 return self.bitarray.bytes 207 else: 208 return self.wr_bitarray.bytes 209 210 def get(self, from_read=True): 211 self.get_bitstring(from_read=from_read) 212 213 def get_bitstring(self, from_read=True): 214 if from_read: 215 return self.bitarray 216 else: 217 return self.wr_bitarray 218 219 def convert_to_bitstring(self, new_data): 220 if isinstance(new_data, BitArray): 221 return new_data 222 else: 223 return BitArray(bytes=new_data, length=len(new_data) * 8) 224 225 def get_words(self): 226 def get_offsets(self): 227 return [x + self.rd_addr for x in range(0, self.get_block_len(), 4)] 228 229 return [self.parent.read_reg(offs) for offs in get_offsets(self)] 230 231 def read(self, print_info=True): 232 words = self.get_words() 233 data = BitArray() 234 for word in reversed(words): 235 data.append("uint:32=%d" % word) 236 self.bitarray.overwrite(data, pos=0) 237 if print_info: 238 self.print_block(self.bitarray, "read_regs") 239 240 def print_block(self, bit_string, comment, debug=False): 241 if self.parent.debug or debug: 242 bit_string.pos = 0 243 print( 244 "%-15s (%-16s) [%-2d] %s:" 245 % (self.name, " ".join(self.alias)[:16], self.id, comment), 246 " ".join( 247 [ 248 "%08x" % word 249 for word in bit_string.readlist( 250 "%d*uint:32" % (bit_string.len / 32) 251 )[::-1] 252 ] 253 ), 254 ) 255 256 def check_wr_data(self): 257 wr_data = self.wr_bitarray 258 if wr_data.all(False): 259 # nothing to burn 260 if self.parent.debug: 261 print("[{:02}] {:20} nothing to burn".format(self.id, self.name)) 262 return False 263 if len(wr_data.bytes) != len(self.bitarray.bytes): 264 raise esptool.FatalError( 265 "Data does not fit: the block%d size is %d bytes, data is %d bytes" 266 % (self.id, len(self.bitarray.bytes), len(wr_data.bytes)) 267 ) 268 self.check_wr_rd_protect() 269 270 if self.get_bitstring().all(False): 271 print( 272 "[{:02}] {:20} is empty, will burn the new value".format( 273 self.id, self.name 274 ) 275 ) 276 else: 277 # the written block in chip is not empty 278 if self.get_bitstring() == wr_data: 279 print( 280 "[{:02}] {:20} is already written the same value, " 281 "continue with EMPTY_BLOCK".format(self.id, self.name) 282 ) 283 wr_data.set(0) 284 else: 285 print("[{:02}] {:20} is not empty".format(self.id, self.name)) 286 print("\t(written ):", self.get_bitstring()) 287 print("\t(to write):", wr_data) 288 mask = self.get_bitstring() & wr_data 289 if mask == wr_data: 290 print( 291 "\tAll wr_data bits are set in the written block, " 292 "continue with EMPTY_BLOCK." 293 ) 294 wr_data.set(0) 295 else: 296 coding_scheme = self.get_coding_scheme() 297 if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE: 298 print("\t(coding scheme = NONE)") 299 elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS: 300 print("\t(coding scheme = RS)") 301 error_msg = ( 302 "\tBurn into %s is forbidden " 303 "(RS coding scheme does not allow this)." % (self.name) 304 ) 305 self.parent.print_error_msg(error_msg) 306 elif coding_scheme == self.parent.REGS.CODING_SCHEME_34: 307 print("\t(coding scheme = 3/4)") 308 data_can_not_be_burn = False 309 for i in range(0, self.get_bitstring().len, 6 * 8): 310 rd_chunk = self.get_bitstring()[i : i + 6 * 8 :] 311 wr_chunk = wr_data[i : i + 6 * 8 :] 312 if rd_chunk.any(True): 313 if wr_chunk.any(True): 314 print( 315 "\twritten chunk [%d] and wr_chunk " 316 "are not empty. " % (i // (6 * 8)), 317 end="", 318 ) 319 if rd_chunk == wr_chunk: 320 print( 321 "wr_chunk == rd_chunk. " 322 "Continue with empty chunk." 323 ) 324 wr_data[i : i + 6 * 8 :].set(0) 325 else: 326 print("wr_chunk != rd_chunk. Can not burn.") 327 print("\twritten ", rd_chunk) 328 print("\tto write", wr_chunk) 329 data_can_not_be_burn = True 330 if data_can_not_be_burn: 331 error_msg = ( 332 "\tBurn into %s is forbidden " 333 "(3/4 coding scheme does not allow this)." % (self.name) 334 ) 335 self.parent.print_error_msg(error_msg) 336 else: 337 raise esptool.FatalError( 338 "The coding scheme ({}) is not supported".format( 339 coding_scheme 340 ) 341 ) 342 343 def save(self, new_data): 344 # new_data will be checked by check_wr_data() during burn_all() 345 # new_data (bytes) = [0][1][2] ... [N] (original data) 346 # in string format = [0] [1] [2] ... [N] (util.hexify(data, " ")) 347 # in hex format = 0x[N]....[2][1][0] (from bitstring print(data)) 348 # in reg format = [3][2][1][0] ... [N][][][] (as it will be in the device) 349 # in bitstring = [N] ... [2][1][0] (to get a correct bitstring 350 # need to reverse new_data) 351 # *[x] - means a byte. 352 data = BitStream(bytes=new_data[::-1], length=len(new_data) * 8) 353 if self.parent.debug: 354 print( 355 "\twritten : {} ->\n\tto write: {}".format(self.get_bitstring(), data) 356 ) 357 self.wr_bitarray.overwrite(self.wr_bitarray | data, pos=0) 358 359 def burn_words(self, words): 360 for burns in range(3): 361 self.parent.efuse_controller_setup() 362 if self.parent.debug: 363 print("Write data to BLOCK%d" % (self.id)) 364 write_reg_addr = self.wr_addr 365 for word in words: 366 # for ep32s2: using EFUSE_PGM_DATA[0..7]_REG for writing data 367 # 32 bytes to EFUSE_PGM_DATA[0..7]_REG 368 # 12 bytes to EFUSE_CHECK_VALUE[0..2]_REG. These regs are next after 369 # EFUSE_PGM_DATA_REG 370 # for esp32: 371 # each block has the special regs EFUSE_BLK[0..3]_WDATA[0..7]_REG 372 # for writing data 373 if self.parent.debug: 374 print("Addr 0x%08x, data=0x%08x" % (write_reg_addr, word)) 375 self.parent.write_reg(write_reg_addr, word) 376 write_reg_addr += 4 377 378 self.parent.write_efuses(self.id) 379 for _ in range(5): 380 self.parent.efuse_read() 381 self.parent.get_coding_scheme_warnings(silent=True) 382 if self.fail or self.num_errors: 383 print( 384 "Error in BLOCK%d, re-burn it again (#%d), to fix it. " 385 "fail_bit=%d, num_errors=%d" 386 % (self.id, burns, self.fail, self.num_errors) 387 ) 388 break 389 if not self.fail and self.num_errors == 0: 390 self.read(print_info=False) 391 if self.wr_bitarray & self.bitarray != self.wr_bitarray: 392 # if the required bits are not set then we need to re-burn it again. 393 if burns < 2: 394 print( 395 f"\nRepeat burning BLOCK{self.id} (#{burns + 2}) because not all bits were set" 396 ) 397 continue 398 else: 399 print( 400 f"\nAfter {burns + 1} attempts, the required data was not set to BLOCK{self.id}" 401 ) 402 break 403 404 def burn(self): 405 if self.wr_bitarray.all(False): 406 # nothing to burn 407 return 408 before_burn_bitarray = self.bitarray[:] 409 assert before_burn_bitarray is not self.bitarray 410 self.print_block(self.wr_bitarray, "to_write") 411 words = self.apply_coding_scheme() 412 self.burn_words(words) 413 self.read() 414 if not self.is_readable(): 415 print( 416 "{} ({}) is read-protected. " 417 "Read back the burn value is not possible.".format( 418 self.name, self.alias 419 ) 420 ) 421 if self.bitarray.all(False): 422 print("Read all '0'") 423 else: 424 # Should never happen 425 raise esptool.FatalError( 426 "The {} is read-protected but not all '0' ({})".format( 427 self.name, self.bitarray.hex 428 ) 429 ) 430 else: 431 if self.wr_bitarray == self.bitarray: 432 print("BURN BLOCK%-2d - OK (write block == read block)" % self.id) 433 elif ( 434 self.wr_bitarray & self.bitarray == self.wr_bitarray 435 and self.bitarray & before_burn_bitarray == before_burn_bitarray 436 ): 437 print("BURN BLOCK%-2d - OK (all write block bits are set)" % self.id) 438 else: 439 # Happens only when an efuse is written and read-protected 440 # in one command 441 self.print_block(self.wr_bitarray, "Expected") 442 self.print_block(self.bitarray, "Real ") 443 # Read-protected BLK0 values are reported back as zeros, 444 # raise error only for other blocks 445 if self.id != 0: 446 raise esptool.FatalError( 447 "Burn {} ({}) was not successful".format(self.name, self.alias) 448 ) 449 self.wr_bitarray.set(0) 450 451 452class EspEfusesBase(object): 453 """ 454 Wrapper object to manage the efuse fields in a connected ESP bootloader 455 """ 456 457 _esp = None 458 blocks: List[EfuseBlockBase] = [] 459 efuses: List = [] 460 coding_scheme = None 461 force_write_always = None 462 batch_mode_cnt = 0 463 postpone = False 464 465 def __iter__(self): 466 return self.efuses.__iter__() 467 468 def get_crystal_freq(self): 469 return self._esp.get_crystal_freq() 470 471 def read_efuse(self, n): 472 """Read the nth word of the ESP3x EFUSE region.""" 473 return self._esp.read_efuse(n) 474 475 def read_reg(self, addr): 476 return self._esp.read_reg(addr) 477 478 def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0): 479 return self._esp.write_reg(addr, value, mask, delay_us, delay_after_us) 480 481 def update_reg(self, addr, mask, new_val): 482 return self._esp.update_reg(addr, mask, new_val) 483 484 def efuse_controller_setup(self): 485 pass 486 487 def reconnect_chip(self, esp): 488 print("Re-connecting...") 489 baudrate = esp._port.baudrate 490 port = esp._port.port 491 esp._port.close() 492 return esptool.cmds.detect_chip(port, baudrate) 493 494 def get_index_block_by_name(self, name): 495 for block in self.blocks: 496 if block.name == name or name in block.alias: 497 return block.id 498 return None 499 500 def read_blocks(self): 501 for block in self.blocks: 502 block.read() 503 504 def update_efuses(self): 505 for efuse in self.efuses: 506 efuse.update(self.blocks[efuse.block].bitarray) 507 508 def postpone_efuses_from_block0_to_burn(self, block): 509 postpone_efuses = {} 510 511 if block.id != 0: 512 return postpone_efuses 513 514 # We need to check this list of efuses. If we are going to burn an efuse 515 # from this list, then we need to split the burn operation into two 516 # steps. The first step involves burning efuses not in this list. In 517 # case of an error during this step, we can recover by burning the 518 # efuses from this list at the very end. This approach provides the 519 # ability to recover efuses if an error occurs during the initial burn 520 # operation. 521 522 # List the efuses here that must be burned at the very end, such as read 523 # and write protection fields, as well as efuses that disable 524 # communication with the espefuse tool. 525 efuses_list = ["WR_DIS", "RD_DIS"] 526 if self._esp.CHIP_NAME == "ESP32": 527 # Efuses below disables communication with the espefuse tool. 528 efuses_list.append("UART_DOWNLOAD_DIS") 529 # other efuses that are better to burn at the very end. 530 efuses_list.append("ABS_DONE_1") 531 efuses_list.append("FLASH_CRYPT_CNT") 532 else: 533 # Efuses below disables communication with the espefuse tool. 534 efuses_list.append("ENABLE_SECURITY_DOWNLOAD") 535 efuses_list.append("DIS_DOWNLOAD_MODE") 536 # other efuses that are better to burn at the very end. 537 efuses_list.append("SPI_BOOT_CRYPT_CNT") 538 efuses_list.append("SECURE_BOOT_EN") 539 540 def get_raw_value_from_write(self, efuse_name): 541 return self[efuse_name].get_bitstring(from_read=False) 542 543 for efuse_name in efuses_list: 544 postpone_efuses[efuse_name] = get_raw_value_from_write(self, efuse_name) 545 546 if any(value != 0 for value in postpone_efuses.values()): 547 if self.debug: 548 print("These BLOCK0 efuses will be burned later at the very end:") 549 print(postpone_efuses) 550 # exclude these efuses from the first burn (postpone them till the end). 551 for key_name in postpone_efuses.keys(): 552 self[key_name].reset() 553 return postpone_efuses 554 555 def recover_postponed_efuses_from_block0_to_burn(self, postpone_efuses): 556 if any(value != 0 for value in postpone_efuses.values()): 557 print("Burn postponed efuses from BLOCK0.") 558 for key_name in postpone_efuses.keys(): 559 self[key_name].save(postpone_efuses[key_name]) 560 561 def burn_all(self, check_batch_mode=False): 562 if check_batch_mode: 563 if self.batch_mode_cnt != 0: 564 print( 565 "\nBatch mode is enabled, " 566 "the burn will be done at the end of the command." 567 ) 568 return False 569 print("\nCheck all blocks for burn...") 570 print("idx, BLOCK_NAME, Conclusion") 571 have_wr_data_for_burn = False 572 for block in self.blocks: 573 block.check_wr_data() 574 if not have_wr_data_for_burn and block.get_bitstring(from_read=False).any( 575 True 576 ): 577 have_wr_data_for_burn = True 578 if not have_wr_data_for_burn: 579 print("Nothing to burn, see messages above.") 580 return True 581 EspEfusesBase.confirm("", self.do_not_confirm) 582 583 def burn_block(block, postponed_efuses): 584 old_fail = block.fail 585 old_num_errors = block.num_errors 586 block.burn() 587 if (block.fail and old_fail != block.fail) or ( 588 block.num_errors and block.num_errors > old_num_errors 589 ): 590 if postponed_efuses: 591 print("The postponed efuses were not burned due to an error.") 592 print("\t1. Try to fix a coding error by this cmd:") 593 print("\t 'espefuse.py check_error --recovery'") 594 command_string = " ".join( 595 f"{key} {value}" 596 for key, value in postponed_efuses.items() 597 if value.any(True) 598 ) 599 print("\t2. Then run the cmd to burn all postponed efuses:") 600 print(f"\t 'espefuse.py burn_efuse {command_string}'") 601 602 raise esptool.FatalError("Error(s) were detected in eFuses") 603 604 # Burn from BLKn -> BLK0. Because BLK0 can set rd or/and wr protection bits. 605 for block in reversed(self.blocks): 606 postponed_efuses = ( 607 self.postpone_efuses_from_block0_to_burn(block) 608 if self.postpone 609 else None 610 ) 611 612 burn_block(block, postponed_efuses) 613 614 if postponed_efuses: 615 self.recover_postponed_efuses_from_block0_to_burn(postponed_efuses) 616 burn_block(block, postponed_efuses) 617 618 print("Reading updated efuses...") 619 self.read_coding_scheme() 620 self.read_blocks() 621 self.update_efuses() 622 return True 623 624 @staticmethod 625 def confirm(action, do_not_confirm): 626 print( 627 "%s%s\nThis is an irreversible operation!" 628 % (action, "" if action.endswith("\n") else ". ") 629 ) 630 if not do_not_confirm: 631 print("Type 'BURN' (all capitals) to continue.") 632 # required for Pythons which disable line buffering, ie mingw in mintty 633 sys.stdout.flush() 634 yes = input() 635 if yes != "BURN": 636 print("Aborting.") 637 sys.exit(0) 638 639 def print_error_msg(self, error_msg): 640 if self.force_write_always is not None: 641 if not self.force_write_always: 642 error_msg += "(use '--force-write-always' option to ignore it)" 643 if self.force_write_always: 644 print(error_msg, "Skipped because '--force-write-always' option.") 645 else: 646 raise esptool.FatalError(error_msg) 647 648 def get_block_errors(self, block_num): 649 """Returns (error count, failure boolean flag)""" 650 return self.blocks[block_num].num_errors, self.blocks[block_num].fail 651 652 653class EfuseFieldBase(EfuseProtectBase): 654 def __init__(self, parent, param): 655 self.category = param.category 656 self.parent = parent 657 self.block = param.block 658 self.word = param.word 659 self.pos = param.pos 660 self.write_disable_bit = param.write_disable_bit 661 self.read_disable_bit = param.read_disable_bit 662 self.name = param.name 663 self.efuse_class = param.class_type 664 self.efuse_type = param.type 665 self.description = param.description 666 self.dict_value = param.dictionary 667 self.bit_len = param.bit_len 668 self.alt_names = param.alt_names 669 self.fail = False 670 self.num_errors = 0 671 self.bitarray = BitStream(self.bit_len) 672 self.bitarray.set(0) 673 self.update(self.parent.blocks[self.block].bitarray) 674 675 def is_field_calculated(self): 676 return self.word is None or self.pos is None 677 678 def check_format(self, new_value_str): 679 if new_value_str is None: 680 return new_value_str 681 else: 682 if self.efuse_type.startswith("bytes"): 683 if new_value_str.startswith("0x"): 684 # cmd line: 0x0102030405060708 .... 112233ff (hex) 685 # regs: 112233ff ... 05060708 01020304 686 # BLK: ff 33 22 11 ... 08 07 06 05 04 03 02 01 687 return binascii.unhexlify(new_value_str[2:])[::-1] 688 else: 689 # cmd line: 0102030405060708 .... 112233ff (string) 690 # regs: 04030201 08070605 ... ff332211 691 # BLK: 01 02 03 04 05 06 07 08 ... 11 22 33 ff 692 return binascii.unhexlify(new_value_str) 693 else: 694 return new_value_str 695 696 def convert_to_bitstring(self, new_value): 697 if isinstance(new_value, BitArray): 698 return new_value 699 else: 700 if self.efuse_type.startswith("bytes"): 701 # new_value (bytes) = [0][1][2] ... [N] 702 # (original data) 703 # in string format = [0] [1] [2] ... [N] 704 # (util.hexify(data, " ")) 705 # in hex format = 0x[N]....[2][1][0] 706 # (from bitstring print(data)) 707 # in reg format = [3][2][1][0] ... [N][][][] 708 # (as it will be in the device) 709 # in bitstring = [N] ... [2][1][0] 710 # (to get a correct bitstring need to reverse new_value) 711 # *[x] - means a byte. 712 return BitArray(bytes=new_value[::-1], length=len(new_value) * 8) 713 else: 714 try: 715 return BitArray(self.efuse_type + "={}".format(new_value)) 716 except CreationError as err: 717 print( 718 "New value '{}' is not suitable for {} ({})".format( 719 new_value, self.name, self.efuse_type 720 ) 721 ) 722 raise esptool.FatalError(err) 723 724 def check_new_value(self, bitarray_new_value): 725 bitarray_old_value = self.get_bitstring() | self.get_bitstring(from_read=False) 726 727 if not bitarray_new_value.any(True) and not bitarray_old_value.any(True): 728 return 729 730 if bitarray_new_value.len != bitarray_old_value.len: 731 raise esptool.FatalError( 732 "For {} efuse, the length of the new value is wrong, " 733 "expected {} bits, was {} bits.".format( 734 self.name, bitarray_old_value.len, bitarray_new_value.len 735 ) 736 ) 737 if ( 738 bitarray_new_value == bitarray_old_value 739 or bitarray_new_value & self.get_bitstring() == bitarray_new_value 740 ): 741 error_msg = "\tThe same value for {} ".format(self.name) 742 error_msg += "is already burned. Do not change the efuse." 743 print(error_msg) 744 bitarray_new_value.set(0) 745 elif bitarray_new_value == self.get_bitstring(from_read=False): 746 error_msg = "\tThe same value for {} ".format(self.name) 747 error_msg += "is already prepared for the burn operation." 748 print(error_msg) 749 bitarray_new_value.set(0) 750 else: 751 if self.name not in ["WR_DIS", "RD_DIS"]: 752 # WR_DIS, RD_DIS fields can have already set bits. 753 # Do not need to check below condition for them. 754 if bitarray_new_value | bitarray_old_value != bitarray_new_value: 755 error_msg = "\tNew value contains some bits that cannot be cleared " 756 error_msg += "(value will be {})".format( 757 bitarray_old_value | bitarray_new_value 758 ) 759 self.parent.print_error_msg(error_msg) 760 self.check_wr_rd_protect() 761 762 def save_to_block(self, bitarray_field): 763 block = self.parent.blocks[self.block] 764 wr_bitarray_temp = block.wr_bitarray.copy() 765 position = wr_bitarray_temp.length - ( 766 self.word * 32 + self.pos + bitarray_field.len 767 ) 768 wr_bitarray_temp.overwrite(bitarray_field, pos=position) 769 block.wr_bitarray |= wr_bitarray_temp 770 771 def save(self, new_value): 772 bitarray_field = self.convert_to_bitstring(new_value) 773 self.check_new_value(bitarray_field) 774 self.save_to_block(bitarray_field) 775 776 def update(self, bit_array_block): 777 if self.is_field_calculated(): 778 self.bitarray.overwrite( 779 self.convert_to_bitstring(self.check_format(self.get())), pos=0 780 ) 781 return 782 field_len = self.bitarray.len 783 bit_array_block.pos = bit_array_block.length - ( 784 self.word * 32 + self.pos + field_len 785 ) 786 self.bitarray.overwrite(bit_array_block.read(field_len), pos=0) 787 err_bitarray = self.parent.blocks[self.block].err_bitarray 788 if err_bitarray is not None: 789 err_bitarray.pos = err_bitarray.length - ( 790 self.word * 32 + self.pos + field_len 791 ) 792 self.fail = not err_bitarray.read(field_len).all(False) 793 else: 794 self.fail = self.parent.blocks[self.block].fail 795 self.num_errors = self.parent.blocks[self.block].num_errors 796 797 def get_raw(self, from_read=True): 798 """Return the raw (unformatted) numeric value of the efuse bits 799 800 Returns a simple integer or (for some subclasses) a bitstring. 801 type: int or bool -> int 802 type: bytes -> bytearray 803 """ 804 return self.get_bitstring(from_read).read(self.efuse_type) 805 806 def get(self, from_read=True): 807 """Get a formatted version of the efuse value, suitable for display 808 type: int or bool -> int 809 type: bytes -> string "01 02 03 04 05 06 07 08 ... ". 810 Byte order [0] ... [N]. dump regs: 0x04030201 0x08070605 ... 811 """ 812 if self.efuse_type.startswith("bytes"): 813 return util.hexify(self.get_bitstring(from_read).bytes[::-1], " ") 814 else: 815 return self.get_raw(from_read) 816 817 def get_meaning(self, from_read=True): 818 """Get the meaning of efuse from dict if possible, suitable for display""" 819 if self.dict_value: 820 try: 821 return self.dict_value[self.get_raw(from_read)] 822 except KeyError: 823 pass 824 return self.get(from_read) 825 826 def get_bitstring(self, from_read=True): 827 if from_read: 828 self.bitarray.pos = 0 829 return self.bitarray 830 else: 831 field_len = self.bitarray.len 832 block = self.parent.blocks[self.block] 833 block.wr_bitarray.pos = block.wr_bitarray.length - ( 834 self.word * 32 + self.pos + field_len 835 ) 836 return block.wr_bitarray.read(self.bitarray.len) 837 838 def burn(self, new_value): 839 # Burn a efuse. Added for compatibility reason. 840 self.save(new_value) 841 self.parent.burn_all() 842 843 def get_info(self): 844 output = f"{self.name} (BLOCK{self.block})" 845 if self.block == 0: 846 if self.fail: 847 output += "[error]" 848 else: 849 errs, fail = self.parent.get_block_errors(self.block) 850 if errs != 0 or fail: 851 output += "[error]" 852 if self.efuse_class == "keyblock": 853 name = self.parent.blocks[self.block].key_purpose_name 854 if name is not None: 855 output += f"\n Purpose: {self.parent[name].get()}\n " 856 return output 857 858 def reset(self): 859 # resets a efuse that is prepared for burning 860 bitarray_field = self.convert_to_bitstring(0) 861 block = self.parent.blocks[self.block] 862 wr_bitarray_temp = block.wr_bitarray.copy() 863 position = wr_bitarray_temp.length - ( 864 self.word * 32 + self.pos + bitarray_field.len 865 ) 866 wr_bitarray_temp.overwrite(bitarray_field, pos=position) 867 block.wr_bitarray = wr_bitarray_temp 868