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