1# This file includes the common operations with eFuses 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 argparse 8import json 9import sys 10 11from bitstring import BitStream 12 13import esptool 14 15from . import base_fields 16from . import util 17 18 19def add_common_commands(subparsers, efuses): 20 class ActionEfuseValuePair(argparse.Action): 21 def __init__(self, option_strings, dest, nargs=None, **kwargs): 22 self._nargs = nargs 23 self._choices = kwargs.get("efuse_choices") 24 self.efuses = kwargs.get("efuses") 25 del kwargs["efuse_choices"] 26 del kwargs["efuses"] 27 super(ActionEfuseValuePair, self).__init__( 28 option_strings, dest, nargs=nargs, **kwargs 29 ) 30 31 def __call__(self, parser, namespace, values, option_string=None): 32 def check_efuse_name(efuse_name, efuse_list): 33 if efuse_name not in self._choices: 34 raise esptool.FatalError( 35 "Invalid the efuse name '{}'. " 36 "Available the efuse names: {}".format( 37 efuse_name, self._choices 38 ) 39 ) 40 41 efuse_value_pairs = {} 42 if len(values) > 1: 43 if len(values) % 2: 44 raise esptool.FatalError( 45 "The list does not have a valid pair (name value) {}".format( 46 values 47 ) 48 ) 49 for i in range(0, len(values), 2): 50 efuse_name, new_value = values[i : i + 2 :] 51 check_efuse_name(efuse_name, self._choices) 52 check_arg = base_fields.CheckArgValue(self.efuses, efuse_name) 53 efuse_value_pairs[efuse_name] = check_arg(new_value) 54 else: 55 # For the case of compatibility, when only the efuse_name is given 56 # Fields with 'bitcount' and 'bool' types can be without new_value arg 57 efuse_name = values[0] 58 check_efuse_name(efuse_name, self._choices) 59 check_arg = base_fields.CheckArgValue(self.efuses, efuse_name) 60 efuse_value_pairs[efuse_name] = check_arg(None) 61 setattr(namespace, self.dest, efuse_value_pairs) 62 63 burn = subparsers.add_parser( 64 "burn_efuse", help="Burn the efuse with the specified name" 65 ) 66 burn.add_argument( 67 "name_value_pairs", 68 help="Name of efuse field and new value pairs to burn. EFUSE_NAME: " 69 "[{}].".format(", ".join([e.name for e in efuses.efuses])), 70 action=ActionEfuseValuePair, 71 nargs="+", 72 metavar="[EFUSE_NAME VALUE]", 73 efuse_choices=[e.name for e in efuses.efuses] 74 + [name for e in efuses.efuses for name in e.alt_names if name != ""], 75 efuses=efuses, 76 ) 77 78 read_protect_efuse = subparsers.add_parser( 79 "read_protect_efuse", 80 help="Disable readback for the efuse with the specified name", 81 ) 82 read_protect_efuse.add_argument( 83 "efuse_name", 84 help="Name of efuse register to burn", 85 nargs="+", 86 choices=[e.name for e in efuses.efuses if e.read_disable_bit is not None] 87 + [ 88 name 89 for e in efuses.efuses 90 if e.read_disable_bit is not None 91 for name in e.alt_names 92 if name != "" 93 ], 94 ) 95 96 write_protect_efuse = subparsers.add_parser( 97 "write_protect_efuse", 98 help="Disable writing to the efuse with the specified name", 99 ) 100 write_protect_efuse.add_argument( 101 "efuse_name", 102 help="Name of efuse register to burn", 103 nargs="+", 104 choices=[e.name for e in efuses.efuses if e.write_disable_bit is not None] 105 + [ 106 name 107 for e in efuses.efuses 108 if e.write_disable_bit is not None 109 for name in e.alt_names 110 if name != "" 111 ], 112 ) 113 114 burn_block_data = subparsers.add_parser( 115 "burn_block_data", 116 help="Burn non-key data to EFUSE blocks. " 117 "(Don't use this command to burn key data for Flash Encryption or " 118 "ESP32 Secure Boot V1, as the byte order of keys is swapped (use burn_key)).", 119 ) 120 add_force_write_always(burn_block_data) 121 burn_block_data.add_argument( 122 "--offset", "-o", help="Byte offset in the efuse block", type=int, default=0 123 ) 124 burn_block_data.add_argument( 125 "block", 126 help="Efuse block to burn.", 127 action="append", 128 choices=efuses.BURN_BLOCK_DATA_NAMES, 129 ) 130 burn_block_data.add_argument( 131 "datafile", 132 help="File containing data to burn into the efuse block", 133 action="append", 134 type=argparse.FileType("rb"), 135 ) 136 for _ in range(0, len(efuses.BURN_BLOCK_DATA_NAMES)): 137 burn_block_data.add_argument( 138 "block", 139 help="Efuse block to burn.", 140 metavar="BLOCK", 141 nargs="?", 142 action="append", 143 choices=efuses.BURN_BLOCK_DATA_NAMES, 144 ) 145 burn_block_data.add_argument( 146 "datafile", 147 nargs="?", 148 help="File containing data to burn into the efuse block", 149 metavar="DATAFILE", 150 action="append", 151 type=argparse.FileType("rb"), 152 ) 153 154 set_bit_cmd = subparsers.add_parser("burn_bit", help="Burn bit in the efuse block.") 155 add_force_write_always(set_bit_cmd) 156 set_bit_cmd.add_argument( 157 "block", help="Efuse block to burn.", choices=efuses.BURN_BLOCK_DATA_NAMES 158 ) 159 set_bit_cmd.add_argument( 160 "bit_number", 161 help="Bit number in the efuse block [0..BLK_LEN-1]", 162 nargs="+", 163 type=int, 164 ) 165 166 subparsers.add_parser( 167 "adc_info", 168 help="Display information about ADC calibration data stored in efuse.", 169 ) 170 171 dump_cmd = subparsers.add_parser("dump", help="Dump raw hex values of all efuses") 172 dump_cmd.add_argument( 173 "--file_name", 174 help="Saves dump for each block into separate file. Provide the common " 175 "path name /path/blk.bin, it will create: blk0.bin, blk1.bin ... blkN.bin. " 176 "Use burn_block_data to write it back to another chip.", 177 ) 178 179 summary_cmd = subparsers.add_parser( 180 "summary", help="Print human-readable summary of efuse values" 181 ) 182 summary_cmd.add_argument( 183 "--format", 184 help="Select the summary format", 185 choices=["summary", "json", "value_only"], 186 default="summary", 187 ) 188 summary_cmd.add_argument( 189 "--file", 190 help="File to save the efuse summary", 191 type=argparse.FileType("w"), 192 default=sys.stdout, 193 ) 194 summary_cmd.add_argument( 195 "efuses_to_show", 196 help="The efuses to show. If not provided, all efuses will be shown.", 197 nargs="*", 198 ) 199 200 execute_scripts = subparsers.add_parser( 201 "execute_scripts", help="Executes scripts to burn at one time." 202 ) 203 execute_scripts.add_argument( 204 "scripts", 205 help="The special format of python scripts.", 206 nargs="+", 207 type=argparse.FileType("r"), 208 ) 209 execute_scripts.add_argument( 210 "--index", 211 help="integer index. " 212 "It allows to retrieve unique data per chip from configfiles " 213 "and then burn them (ex. CUSTOM_MAC, UNIQUE_ID).", 214 type=int, 215 ) 216 execute_scripts.add_argument( 217 "--configfiles", 218 help="List of configfiles with data", 219 nargs="?", 220 action="append", 221 type=argparse.FileType("r"), 222 ) 223 224 check_error_cmd = subparsers.add_parser("check_error", help="Checks eFuse errors") 225 check_error_cmd.add_argument( 226 "--recovery", 227 help="Recovery of BLOCKs after encoding errors", 228 action="store_true", 229 ) 230 231 232def add_force_write_always(p): 233 p.add_argument( 234 "--force-write-always", 235 help="Write the efuse even if it looks like it's already been written, " 236 "or is write protected. Note that this option can't disable write protection, " 237 "or clear any bit which has already been set.", 238 action="store_true", 239 ) 240 241 242def add_show_sensitive_info_option(p): 243 p.add_argument( 244 "--show-sensitive-info", 245 help="Show data to be burned (may expose sensitive data). " 246 "Enabled if --debug is used.", 247 action="store_true", 248 default=False, 249 ) 250 251 252def summary(esp, efuses, args): 253 """Print a human-readable or json summary of efuse contents""" 254 ROW_FORMAT = "%-50s %-50s%s = %s %s %s" 255 human_output = args.format in ["summary", "value_only"] 256 value_only = args.format == "value_only" 257 if value_only and len(args.efuses_to_show) != 1: 258 raise esptool.FatalError( 259 "The 'value_only' format can be used exactly for one efuse." 260 ) 261 do_filtering = bool(args.efuses_to_show) 262 json_efuse = {} 263 summary_efuse = [] 264 if args.file != sys.stdout: 265 print("Saving efuse values to " + args.file.name) 266 if human_output and not value_only: 267 summary_efuse.append( 268 ROW_FORMAT.replace("-50", "-12") 269 % ( 270 "EFUSE_NAME (Block)", 271 "Description", 272 "", 273 "[Meaningful Value]", 274 "[Readable/Writeable]", 275 "(Hex Value)", 276 ) 277 ) 278 summary_efuse.append("-" * 88) 279 for category in sorted(set(e.category for e in efuses), key=lambda c: c.title()): 280 if human_output and not value_only: 281 summary_efuse.append(f"{category.title()} fuses:") 282 for e in (e for e in efuses if e.category == category): 283 if e.efuse_type.startswith("bytes"): 284 raw = "" 285 else: 286 raw = "({})".format(e.get_bitstring()) 287 (readable, writeable) = (e.is_readable(), e.is_writeable()) 288 if readable and writeable: 289 perms = "R/W" 290 elif readable: 291 perms = "R/-" 292 elif writeable: 293 perms = "-/W" 294 else: 295 perms = "-/-" 296 base_value = e.get_meaning() 297 value = str(base_value) 298 if not readable: 299 count_read_disable_bits = e.get_count_read_disable_bits() 300 if count_read_disable_bits == 2: 301 # On the C2 chip, BLOCK_KEY0 has two read protection bits [0, 1] 302 # related to the lower and higher part of the block. 303 v = [value[: (len(value) // 2)], value[(len(value) // 2) :]] 304 for i in range(count_read_disable_bits): 305 if not e.is_readable(blk_part=i): 306 v[i] = v[i].replace("0", "?") 307 value = "".join(v) 308 else: 309 value = value.replace("0", "?") 310 if ( 311 human_output 312 and (not do_filtering or e.name in args.efuses_to_show) 313 and not value_only 314 ): 315 summary_efuse.append( 316 ROW_FORMAT 317 % ( 318 e.get_info(), 319 e.description[:50], 320 "\n " if len(value) > 20 else "", 321 value, 322 perms, 323 raw, 324 ) 325 ) 326 desc_len = len(e.description[50:]) 327 if desc_len: 328 desc_len += 50 329 for i in range(50, desc_len, 50): 330 summary_efuse.append( 331 f"{'':<50} {e.description[i : (50 + i)]:<50}" 332 ) 333 elif human_output and value_only and e.name in args.efuses_to_show: 334 summary_efuse.append(f"{value}") 335 elif args.format == "json" and ( 336 not do_filtering or e.name in args.efuses_to_show 337 ): 338 json_efuse[e.name] = { 339 "name": e.name, 340 "value": base_value if readable else value, 341 "readable": readable, 342 "writeable": writeable, 343 "description": e.description, 344 "category": e.category, 345 "block": e.block, 346 "word": e.word, 347 "pos": e.pos, 348 "efuse_type": e.efuse_type, 349 "bit_len": e.bit_len, 350 } 351 if human_output and not value_only: 352 # Remove empty category if efuses are filtered and there are none to show 353 if do_filtering and summary_efuse[-1] == f"{category.title()} fuses:": 354 summary_efuse.pop() 355 else: 356 summary_efuse.append("") 357 if human_output and not value_only: 358 summary_efuse.append(efuses.summary()) 359 warnings = efuses.get_coding_scheme_warnings() 360 if warnings: 361 summary_efuse.append( 362 "WARNING: Coding scheme has encoding bit error warnings" 363 ) 364 if human_output: 365 for line in summary_efuse: 366 print(line, file=args.file) 367 if args.file != sys.stdout: 368 args.file.close() 369 print("Done") 370 elif args.format == "json": 371 json.dump(json_efuse, args.file, sort_keys=True, indent=4) 372 print("") 373 374 375def dump(esp, efuses, args): 376 """Dump raw efuse data registers""" 377 # Using --debug option allows to print dump. 378 # Nothing to do here. The log will be printed 379 # during EspEfuses.__init__() in self.read_blocks() 380 if args.file_name: 381 # save dump to the file 382 for block in efuses.blocks: 383 file_dump_name = args.file_name 384 place_for_index = file_dump_name.find(".bin") 385 file_dump_name = ( 386 file_dump_name[:place_for_index] 387 + str(block.id) 388 + file_dump_name[place_for_index:] 389 ) 390 print(file_dump_name) 391 with open(file_dump_name, "wb") as f: 392 block.get_bitstring().byteswap() 393 block.get_bitstring().tofile(f) 394 395 396def burn_efuse(esp, efuses, args): 397 def print_attention(blocked_efuses_after_burn): 398 if len(blocked_efuses_after_burn): 399 print( 400 " ATTENTION! This BLOCK uses NOT the NONE coding scheme " 401 "and after 'BURN', these efuses can not be burned in the feature:" 402 ) 403 for i in range(0, len(blocked_efuses_after_burn), 5): 404 print( 405 " ", 406 "".join("{}".format(blocked_efuses_after_burn[i : i + 5 :])), 407 ) 408 409 efuse_name_list = [name for name in args.name_value_pairs.keys()] 410 burn_efuses_list = [efuses[name] for name in efuse_name_list] 411 old_value_list = [efuses[name].get_raw() for name in efuse_name_list] 412 new_value_list = [value for value in args.name_value_pairs.values()] 413 util.check_duplicate_name_in_list(efuse_name_list) 414 415 attention = "" 416 print("The efuses to burn:") 417 for block in efuses.blocks: 418 burn_list_a_block = [e for e in burn_efuses_list if e.block == block.id] 419 if len(burn_list_a_block): 420 print(" from BLOCK%d" % (block.id)) 421 for field in burn_list_a_block: 422 print(" - %s" % (field.name)) 423 if ( 424 efuses.blocks[field.block].get_coding_scheme() 425 != efuses.REGS.CODING_SCHEME_NONE 426 ): 427 using_the_same_block_names = [ 428 e.name for e in efuses if e.block == field.block 429 ] 430 wr_names = [e.name for e in burn_list_a_block] 431 blocked_efuses_after_burn = [ 432 name 433 for name in using_the_same_block_names 434 if name not in wr_names 435 ] 436 attention = " (see 'ATTENTION!' above)" 437 if attention: 438 print_attention(blocked_efuses_after_burn) 439 440 print("\nBurning efuses{}:".format(attention)) 441 for efuse, new_value in zip(burn_efuses_list, new_value_list): 442 print( 443 "\n - '{}' ({}) {} -> {}".format( 444 efuse.name, 445 efuse.description, 446 efuse.get_bitstring(), 447 efuse.convert_to_bitstring(new_value), 448 ) 449 ) 450 efuse.save(new_value) 451 452 print() 453 if "ENABLE_SECURITY_DOWNLOAD" in efuse_name_list: 454 print( 455 "ENABLE_SECURITY_DOWNLOAD -> 1: eFuses will not be read back " 456 "for confirmation because this mode disables " 457 "any SRAM and register operations." 458 ) 459 print(" espefuse will not work.") 460 print(" esptool can read/write only flash.") 461 462 if "DIS_DOWNLOAD_MODE" in efuse_name_list: 463 print( 464 "DIS_DOWNLOAD_MODE -> 1: eFuses will not be read back for " 465 "confirmation because this mode disables any communication with the chip." 466 ) 467 print( 468 " espefuse/esptool will not work because " 469 "they will not be able to connect to the chip." 470 ) 471 472 if ( 473 esp.CHIP_NAME == "ESP32" 474 and esp.get_chip_revision() >= 300 475 and "UART_DOWNLOAD_DIS" in efuse_name_list 476 ): 477 print( 478 "UART_DOWNLOAD_DIS -> 1: eFuses will be read for confirmation, " 479 "but after that connection to the chip will become impossible." 480 ) 481 print(" espefuse/esptool will not work.") 482 483 if not efuses.burn_all(check_batch_mode=True): 484 return 485 486 print("Checking efuses...") 487 raise_error = False 488 for efuse, old_value, new_value in zip( 489 burn_efuses_list, old_value_list, new_value_list 490 ): 491 if not efuse.is_readable(): 492 print( 493 "Efuse %s is read-protected. Read back the burn value is not possible." 494 % efuse.name 495 ) 496 else: 497 new_value = efuse.convert_to_bitstring(new_value) 498 burned_value = efuse.get_bitstring() 499 if burned_value != new_value: 500 print( 501 burned_value, 502 "->", 503 new_value, 504 "Efuse %s failed to burn. Protected?" % efuse.name, 505 ) 506 raise_error = True 507 if raise_error: 508 raise esptool.FatalError("The burn was not successful.") 509 else: 510 print("Successful") 511 512 513def read_protect_efuse(esp, efuses, args): 514 util.check_duplicate_name_in_list(args.efuse_name) 515 516 for efuse_name in args.efuse_name: 517 efuse = efuses[efuse_name] 518 if not efuse.is_readable(): 519 print("Efuse %s is already read protected" % efuse.name) 520 else: 521 if esp.CHIP_NAME == "ESP32": 522 if ( 523 efuse_name == "BLOCK2" 524 and not efuses["ABS_DONE_0"].get() 525 and esp.get_chip_revision() >= 300 526 ): 527 if efuses["ABS_DONE_1"].get(): 528 raise esptool.FatalError( 529 "Secure Boot V2 is on (ABS_DONE_1 = True), " 530 "BLOCK2 must be readable, stop this operation!" 531 ) 532 else: 533 print( 534 "If Secure Boot V2 is used, BLOCK2 must be readable, " 535 "please stop this operation!" 536 ) 537 elif esp.CHIP_NAME == "ESP32-C2": 538 error = ( 539 not efuses["XTS_KEY_LENGTH_256"].get() 540 and efuse_name == "BLOCK_KEY0" 541 ) 542 error |= efuses["SECURE_BOOT_EN"].get() and efuse_name in [ 543 "BLOCK_KEY0", 544 "BLOCK_KEY0_HI_128", 545 ] 546 if error: 547 raise esptool.FatalError( 548 "%s must be readable, stop this operation!" % efuse_name 549 ) 550 else: 551 for block in efuses.Blocks.BLOCKS: 552 block = efuses.Blocks.get(block) 553 if block.name == efuse_name and block.key_purpose is not None: 554 if not efuses[block.key_purpose].need_rd_protect( 555 efuses[block.key_purpose].get() 556 ): 557 raise esptool.FatalError( 558 "%s must be readable, stop this operation!" % efuse_name 559 ) 560 break 561 # make full list of which efuses will be disabled 562 # (ie share a read disable bit) 563 all_disabling = [ 564 e for e in efuses if e.read_disable_bit == efuse.read_disable_bit 565 ] 566 names = ", ".join(e.name for e in all_disabling) 567 print( 568 "Permanently read-disabling efuse%s %s" 569 % ("s" if len(all_disabling) > 1 else "", names) 570 ) 571 efuse.disable_read() 572 573 if not efuses.burn_all(check_batch_mode=True): 574 return 575 576 print("Checking efuses...") 577 raise_error = False 578 for efuse_name in args.efuse_name: 579 efuse = efuses[efuse_name] 580 if efuse.is_readable(): 581 print("Efuse %s is not read-protected." % efuse.name) 582 raise_error = True 583 if raise_error: 584 raise esptool.FatalError("The burn was not successful.") 585 else: 586 print("Successful") 587 588 589def write_protect_efuse(esp, efuses, args): 590 util.check_duplicate_name_in_list(args.efuse_name) 591 for efuse_name in args.efuse_name: 592 efuse = efuses[efuse_name] 593 if not efuse.is_writeable(): 594 print("Efuse %s is already write protected" % efuse.name) 595 else: 596 # make full list of which efuses will be disabled 597 # (ie share a write disable bit) 598 all_disabling = [ 599 e for e in efuses if e.write_disable_bit == efuse.write_disable_bit 600 ] 601 names = ", ".join(e.name for e in all_disabling) 602 print( 603 "Permanently write-disabling efuse%s %s" 604 % ("s" if len(all_disabling) > 1 else "", names) 605 ) 606 efuse.disable_write() 607 608 if not efuses.burn_all(check_batch_mode=True): 609 return 610 611 print("Checking efuses...") 612 raise_error = False 613 for efuse_name in args.efuse_name: 614 efuse = efuses[efuse_name] 615 if efuse.is_writeable(): 616 print("Efuse %s is not write-protected." % efuse.name) 617 raise_error = True 618 if raise_error: 619 raise esptool.FatalError("The burn was not successful.") 620 else: 621 print("Successful") 622 623 624def burn_block_data(esp, efuses, args): 625 block_name_list = args.block[ 626 0 : len([name for name in args.block if name is not None]) : 627 ] 628 datafile_list = args.datafile[ 629 0 : len([name for name in args.datafile if name is not None]) : 630 ] 631 efuses.force_write_always = args.force_write_always 632 633 util.check_duplicate_name_in_list(block_name_list) 634 if args.offset and len(block_name_list) > 1: 635 raise esptool.FatalError( 636 "The 'offset' option is not applicable when a few blocks are passed. " 637 "With 'offset', should only one block be used." 638 ) 639 else: 640 offset = args.offset 641 if offset: 642 num_block = efuses.get_index_block_by_name(block_name_list[0]) 643 block = efuses.blocks[num_block] 644 num_bytes = block.get_block_len() 645 if offset >= num_bytes: 646 raise esptool.FatalError( 647 "Invalid offset: the block%d only holds %d bytes." 648 % (block.id, num_bytes) 649 ) 650 if len(block_name_list) != len(datafile_list): 651 raise esptool.FatalError( 652 "The number of block_name (%d) and datafile (%d) should be the same." 653 % (len(block_name_list), len(datafile_list)) 654 ) 655 656 for block_name, datafile in zip(block_name_list, datafile_list): 657 num_block = efuses.get_index_block_by_name(block_name) 658 block = efuses.blocks[num_block] 659 data = datafile.read() 660 num_bytes = block.get_block_len() 661 if offset != 0: 662 data = (b"\x00" * offset) + data 663 data = data + (b"\x00" * (num_bytes - len(data))) 664 if len(data) != num_bytes: 665 raise esptool.FatalError( 666 "Data does not fit: the block%d size is %d bytes, " 667 "data file is %d bytes, offset %d" 668 % (block.id, num_bytes, len(data), offset) 669 ) 670 print( 671 "[{:02}] {:20} size={:02} bytes, offset={:02} - > [{}].".format( 672 block.id, block.name, len(data), offset, util.hexify(data, " ") 673 ) 674 ) 675 block.save(data) 676 677 if not efuses.burn_all(check_batch_mode=True): 678 return 679 print("Successful") 680 681 682def burn_bit(esp, efuses, args): 683 efuses.force_write_always = args.force_write_always 684 num_block = efuses.get_index_block_by_name(args.block) 685 block = efuses.blocks[num_block] 686 data_block = BitStream(block.get_block_len() * 8) 687 data_block.set(0) 688 try: 689 data_block.set(True, args.bit_number) 690 except IndexError: 691 raise esptool.FatalError( 692 "%s has bit_number in [0..%d]" % (args.block, data_block.len - 1) 693 ) 694 data_block.reverse() 695 print( 696 "bit_number: " 697 "[%-03d]........................................................[0]" 698 % (data_block.len - 1) 699 ) 700 print("BLOCK%-2d :" % block.id, data_block) 701 block.print_block(data_block, "regs_to_write", debug=True) 702 block.save(data_block.bytes[::-1]) 703 704 if not efuses.burn_all(check_batch_mode=True): 705 return 706 print("Successful") 707 708 709def get_error_summary(efuses): 710 efuses.get_coding_scheme_warnings() 711 error_in_blocks = any(blk.fail or blk.num_errors != 0 for blk in efuses.blocks) 712 if not error_in_blocks: 713 return False 714 writable = True 715 for blk in efuses.blocks: 716 if blk.fail or blk.num_errors: 717 if blk.id == 0: 718 for field in efuses: 719 if field.block == blk.id and (field.fail or field.num_errors): 720 wr = "writable" if field.is_writeable() else "not writable" 721 writable &= wr == "writable" 722 name = field.name 723 val = field.get() 724 print(f"BLOCK{field.block:<2}: {name:<40} = {val:<8} ({wr})") 725 else: 726 wr = "writable" if blk.is_writeable() else "not writable" 727 writable &= wr == "writable" 728 name = f"{blk.name} [ERRORS:{blk.num_errors} FAIL:{int(blk.fail)}]" 729 val = str(blk.get_bitstring()) 730 print(f"BLOCK{blk.id:<2}: {name:<40} = {val:<8} ({wr})") 731 if not writable and error_in_blocks: 732 print("Not all errors can be fixed because some fields are write-protected!") 733 return True 734 735 736def check_error(esp, efuses, args): 737 error_in_blocks = get_error_summary(efuses) 738 if args.recovery and error_in_blocks: 739 confirmed = False 740 for block in reversed(efuses.blocks): 741 if block.fail or block.num_errors > 0: 742 if not block.get_bitstring().all(False): 743 block.save(block.get_bitstring().bytes[::-1]) 744 if not confirmed: 745 confirmed = True 746 efuses.confirm( 747 "Recovery of block coding errors", args.do_not_confirm 748 ) 749 block.burn() 750 if confirmed: 751 efuses.update_efuses() 752 error_in_blocks = get_error_summary(efuses) 753 if error_in_blocks: 754 raise esptool.FatalError("Error(s) were detected in eFuses") 755 print("No errors detected") 756