1# This file includes the operations with eFuses for ESP32-S3(beta2) chip 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 io 9import os # noqa: F401. It is used in IDF scripts 10import traceback 11 12import espsecure 13 14import esptool 15 16from . import fields 17from .. import util 18from ..base_operations import ( 19 add_common_commands, 20 add_force_write_always, 21 burn_bit, 22 burn_block_data, 23 burn_efuse, 24 check_error, 25 dump, 26 read_protect_efuse, 27 summary, 28 write_protect_efuse, 29) 30 31 32def protect_options(p): 33 p.add_argument( 34 "--no-write-protect", 35 help="Disable write-protecting of the key. The key remains writable. " 36 "(The keys use the RS coding scheme that does not support post-write " 37 "data changes. Forced write can damage RS encoding bits.) " 38 "The write-protecting of keypurposes does not depend on the option, " 39 "it will be set anyway.", 40 action="store_true", 41 ) 42 p.add_argument( 43 "--no-read-protect", 44 help="Disable read-protecting of the key. The key remains readable software." 45 "The key with keypurpose[USER, RESERVED and *_DIGEST] " 46 "will remain readable anyway. " 47 "For the rest keypurposes the read-protection will be defined the option " 48 "(Read-protect by default).", 49 action="store_true", 50 ) 51 52 53def add_commands(subparsers, efuses): 54 add_common_commands(subparsers, efuses) 55 burn_key = subparsers.add_parser( 56 "burn_key", help="Burn the key block with the specified name" 57 ) 58 protect_options(burn_key) 59 add_force_write_always(burn_key) 60 burn_key.add_argument( 61 "block", 62 help="Key block to burn", 63 action="append", 64 choices=efuses.BLOCKS_FOR_KEYS, 65 ) 66 burn_key.add_argument( 67 "keyfile", 68 help="File containing 256 bits of binary key data", 69 action="append", 70 type=argparse.FileType("rb"), 71 ) 72 burn_key.add_argument( 73 "keypurpose", 74 help="Purpose to set.", 75 action="append", 76 choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, 77 ) 78 for _ in efuses.BLOCKS_FOR_KEYS: 79 burn_key.add_argument( 80 "block", 81 help="Key block to burn", 82 nargs="?", 83 action="append", 84 metavar="BLOCK", 85 choices=efuses.BLOCKS_FOR_KEYS, 86 ) 87 burn_key.add_argument( 88 "keyfile", 89 help="File containing 256 bits of binary key data", 90 nargs="?", 91 action="append", 92 metavar="KEYFILE", 93 type=argparse.FileType("rb"), 94 ) 95 burn_key.add_argument( 96 "keypurpose", 97 help="Purpose to set.", 98 nargs="?", 99 action="append", 100 metavar="KEYPURPOSE", 101 choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, 102 ) 103 104 burn_key_digest = subparsers.add_parser( 105 "burn_key_digest", 106 help="Parse a RSA public key and burn the digest to key efuse block", 107 ) 108 protect_options(burn_key_digest) 109 add_force_write_always(burn_key_digest) 110 burn_key_digest.add_argument( 111 "block", 112 help="Key block to burn", 113 action="append", 114 choices=efuses.BLOCKS_FOR_KEYS, 115 ) 116 burn_key_digest.add_argument( 117 "keyfile", 118 help="Key file to digest (PEM format)", 119 action="append", 120 type=argparse.FileType("rb"), 121 ) 122 burn_key_digest.add_argument( 123 "keypurpose", 124 help="Purpose to set.", 125 action="append", 126 choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, 127 ) 128 for _ in efuses.BLOCKS_FOR_KEYS: 129 burn_key_digest.add_argument( 130 "block", 131 help="Key block to burn", 132 nargs="?", 133 action="append", 134 metavar="BLOCK", 135 choices=efuses.BLOCKS_FOR_KEYS, 136 ) 137 burn_key_digest.add_argument( 138 "keyfile", 139 help="Key file to digest (PEM format)", 140 nargs="?", 141 action="append", 142 metavar="KEYFILE", 143 type=argparse.FileType("rb"), 144 ) 145 burn_key_digest.add_argument( 146 "keypurpose", 147 help="Purpose to set.", 148 nargs="?", 149 action="append", 150 metavar="KEYPURPOSE", 151 choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, 152 ) 153 154 p = subparsers.add_parser( 155 "set_flash_voltage", 156 help="Permanently set the internal flash voltage regulator " 157 "to either 1.8V, 3.3V or OFF. This means GPIO45 can be high or low at reset " 158 "without changing the flash voltage.", 159 ) 160 p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) 161 162 p = subparsers.add_parser( 163 "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." 164 ) 165 p.add_argument( 166 "mac", 167 help="Custom MAC Address to burn given in hexadecimal format with bytes " 168 "separated by colons (e.g. AA:CD:EF:01:02:03).", 169 type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), 170 ) 171 add_force_write_always(p) 172 173 p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") 174 175 176def burn_custom_mac(esp, efuses, args): 177 efuses["CUSTOM_MAC"].save(args.mac) 178 if not efuses.burn_all(check_batch_mode=True): 179 return 180 get_custom_mac(esp, efuses, args) 181 print("Successful") 182 183 184def get_custom_mac(esp, efuses, args): 185 print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) 186 187 188def set_flash_voltage(esp, efuses, args): 189 sdio_force = efuses["VDD_SPI_FORCE"] 190 sdio_tieh = efuses["VDD_SPI_TIEH"] 191 sdio_reg = efuses["VDD_SPI_XPD"] 192 193 # check efuses aren't burned in a way which makes this impossible 194 if args.voltage == "OFF" and sdio_reg.get() != 0: 195 raise esptool.FatalError( 196 "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned" 197 ) 198 199 if args.voltage == "1.8V" and sdio_tieh.get() != 0: 200 raise esptool.FatalError( 201 "Can't set regulator to 1.8V is VDD_SPI_TIEH efuse is already burned" 202 ) 203 204 if args.voltage == "OFF": 205 msg = "Disable internal flash voltage regulator (VDD_SPI). " 206 "SPI flash will need to be powered from an external source.\n" 207 "The following efuse is burned: VDD_SPI_FORCE.\n" 208 "It is possible to later re-enable the internal regulator (%s) " % ( 209 "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" 210 ) 211 "by burning an additional efuse" 212 elif args.voltage == "1.8V": 213 msg = "Set internal flash voltage regulator (VDD_SPI) to 1.8V.\n" 214 "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD.\n" 215 "It is possible to later increase the voltage to 3.3V (permanently) " 216 "by burning additional efuse VDD_SPI_TIEH" 217 elif args.voltage == "3.3V": 218 msg = "Enable internal flash voltage regulator (VDD_SPI) to 3.3V.\n" 219 "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD, VDD_SPI_TIEH." 220 print(msg) 221 222 sdio_force.save(1) # Disable GPIO45 223 if args.voltage != "OFF": 224 sdio_reg.save(1) # Enable internal regulator 225 if args.voltage == "3.3V": 226 sdio_tieh.save(1) 227 print("VDD_SPI setting complete.") 228 if not efuses.burn_all(check_batch_mode=True): 229 return 230 print("Successful") 231 232 233def adc_info(esp, efuses, args): 234 print("") 235 # fmt: off 236 if efuses["BLK_VERSION_MAJOR"].get() == 1: 237 print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) 238 239 print("") 240 print("ADC1 readings stored in efuse BLOCK2:") 241 print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) 242 print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) 243 244 print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) 245 print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) 246 247 print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) 248 print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) 249 250 print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) 251 print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) 252 253 print("") 254 print("ADC2 readings stored in efuse BLOCK2:") 255 print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) 256 print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) 257 258 print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) 259 print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) 260 261 print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) 262 print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) 263 264 print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) 265 print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) 266 else: 267 print("BLK_VERSION_MAJOR = {}".format(efuses["BLK_VERSION_MAJOR"].get_meaning())) 268 # fmt: on 269 270 271def key_block_is_unused(block, key_purpose_block): 272 if not block.is_readable() or not block.is_writeable(): 273 return False 274 275 if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): 276 return False 277 278 if not block.get_bitstring().all(False): 279 return False 280 281 return True 282 283 284def get_next_key_block(efuses, current_key_block, block_name_list): 285 key_blocks = [b for b in efuses.blocks if b.key_purpose_name] 286 start = key_blocks.index(current_key_block) 287 288 # Sort key blocks so that we pick the next free block (and loop around if necessary) 289 key_blocks = key_blocks[start:] + key_blocks[0:start] 290 291 # Exclude any other blocks that will be be burned 292 key_blocks = [b for b in key_blocks if b.name not in block_name_list] 293 294 for block in key_blocks: 295 key_purpose_block = efuses[block.key_purpose_name] 296 if key_block_is_unused(block, key_purpose_block): 297 return block 298 299 return None 300 301 302def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): 303 i = keypurpose_list.index("XTS_AES_256_KEY") 304 block_name = block_name_list[i] 305 306 block_num = efuses.get_index_block_by_name(block_name) 307 block = efuses.blocks[block_num] 308 309 data = datafile_list[i].read() 310 if len(data) != 64: 311 raise esptool.FatalError( 312 "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) 313 ) 314 315 key_block_2 = get_next_key_block(efuses, block, block_name_list) 316 if not key_block_2: 317 raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") 318 319 keypurpose_list.append("XTS_AES_256_KEY_1") 320 datafile_list.append(io.BytesIO(data[:32])) 321 block_name_list.append(block_name) 322 323 keypurpose_list.append("XTS_AES_256_KEY_2") 324 datafile_list.append(io.BytesIO(data[32:])) 325 block_name_list.append(key_block_2.name) 326 327 keypurpose_list.pop(i) 328 datafile_list.pop(i) 329 block_name_list.pop(i) 330 331 332def burn_key(esp, efuses, args, digest=None): 333 if digest is None: 334 datafile_list = args.keyfile[ 335 0 : len([name for name in args.keyfile if name is not None]) : 336 ] 337 else: 338 datafile_list = digest[0 : len([name for name in digest if name is not None]) :] 339 efuses.force_write_always = args.force_write_always 340 block_name_list = args.block[ 341 0 : len([name for name in args.block if name is not None]) : 342 ] 343 keypurpose_list = args.keypurpose[ 344 0 : len([name for name in args.keypurpose if name is not None]) : 345 ] 346 347 if "XTS_AES_256_KEY" in keypurpose_list: 348 # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into 349 # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 350 split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) 351 352 util.check_duplicate_name_in_list(block_name_list) 353 if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( 354 keypurpose_list 355 ): 356 raise esptool.FatalError( 357 "The number of blocks (%d), datafile (%d) and keypurpose (%d) " 358 "should be the same." 359 % (len(block_name_list), len(datafile_list), len(keypurpose_list)) 360 ) 361 362 print("Burn keys to blocks:") 363 for block_name, datafile, keypurpose in zip( 364 block_name_list, datafile_list, keypurpose_list 365 ): 366 efuse = None 367 for block in efuses.blocks: 368 if block_name == block.name or block_name in block.alias: 369 efuse = efuses[block.name] 370 if efuse is None: 371 raise esptool.FatalError("Unknown block name - %s" % (block_name)) 372 num_bytes = efuse.bit_len // 8 373 374 block_num = efuses.get_index_block_by_name(block_name) 375 block = efuses.blocks[block_num] 376 377 if digest is None: 378 data = datafile.read() 379 else: 380 data = datafile 381 382 print(" - %s" % (efuse.name), end=" ") 383 revers_msg = None 384 if efuses[block.key_purpose_name].need_reverse(keypurpose): 385 revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" 386 data = data[::-1] 387 print("-> [%s]" % (util.hexify(data, " "))) 388 if revers_msg: 389 print(revers_msg) 390 if len(data) != num_bytes: 391 raise esptool.FatalError( 392 "Incorrect key file size %d. Key file must be %d bytes (%d bits) " 393 "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) 394 ) 395 396 if efuses[block.key_purpose_name].need_rd_protect(keypurpose): 397 read_protect = False if args.no_read_protect else True 398 else: 399 read_protect = False 400 write_protect = not args.no_write_protect 401 402 # using efuse instead of a block gives the advantage of 403 # checking it as the whole field. 404 efuse.save(data) 405 406 disable_wr_protect_key_purpose = False 407 if efuses[block.key_purpose_name].get() != keypurpose: 408 if efuses[block.key_purpose_name].is_writeable(): 409 print( 410 "\t'%s': '%s' -> '%s'." 411 % ( 412 block.key_purpose_name, 413 efuses[block.key_purpose_name].get(), 414 keypurpose, 415 ) 416 ) 417 efuses[block.key_purpose_name].save(keypurpose) 418 disable_wr_protect_key_purpose = True 419 else: 420 raise esptool.FatalError( 421 "It is not possible to change '%s' to '%s' because " 422 "write protection bit is set." 423 % (block.key_purpose_name, keypurpose) 424 ) 425 else: 426 print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) 427 if efuses[block.key_purpose_name].is_writeable(): 428 disable_wr_protect_key_purpose = True 429 430 if disable_wr_protect_key_purpose: 431 print("\tDisabling write to '%s'." % block.key_purpose_name) 432 efuses[block.key_purpose_name].disable_write() 433 434 if read_protect: 435 print("\tDisabling read to key block") 436 efuse.disable_read() 437 438 if write_protect: 439 print("\tDisabling write to key block") 440 efuse.disable_write() 441 print("") 442 443 if not write_protect: 444 print("Keys will remain writeable (due to --no-write-protect)") 445 if args.no_read_protect: 446 print("Keys will remain readable (due to --no-read-protect)") 447 448 if not efuses.burn_all(check_batch_mode=True): 449 return 450 print("Successful") 451 452 453def burn_key_digest(esp, efuses, args): 454 digest_list = [] 455 datafile_list = args.keyfile[ 456 0 : len([name for name in args.keyfile if name is not None]) : 457 ] 458 block_list = args.block[ 459 0 : len([block for block in args.block if block is not None]) : 460 ] 461 for block_name, datafile in zip(block_list, datafile_list): 462 efuse = None 463 for block in efuses.blocks: 464 if block_name == block.name or block_name in block.alias: 465 efuse = efuses[block.name] 466 if efuse is None: 467 raise esptool.FatalError("Unknown block name - %s" % (block_name)) 468 num_bytes = efuse.bit_len // 8 469 digest = espsecure._digest_sbv2_public_key(datafile) 470 if len(digest) != num_bytes: 471 raise esptool.FatalError( 472 "Incorrect digest size %d. Digest must be %d bytes (%d bits) " 473 "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) 474 ) 475 digest_list.append(digest) 476 burn_key(esp, efuses, args, digest=digest_list) 477 478 479def espefuse(esp, efuses, args, command): 480 parser = argparse.ArgumentParser() 481 subparsers = parser.add_subparsers(dest="operation") 482 add_commands(subparsers, efuses) 483 try: 484 cmd_line_args = parser.parse_args(command.split()) 485 except SystemExit: 486 traceback.print_stack() 487 raise esptool.FatalError('"{}" - incorrect command'.format(command)) 488 if cmd_line_args.operation == "execute_scripts": 489 configfiles = cmd_line_args.configfiles 490 index = cmd_line_args.index 491 # copy arguments from args to cmd_line_args 492 vars(cmd_line_args).update(vars(args)) 493 if cmd_line_args.operation == "execute_scripts": 494 cmd_line_args.configfiles = configfiles 495 cmd_line_args.index = index 496 if cmd_line_args.operation is None: 497 parser.print_help() 498 parser.exit(1) 499 operation_func = globals()[cmd_line_args.operation] 500 # each 'operation' is a module-level function of the same name 501 operation_func(esp, efuses, cmd_line_args) 502 503 504def execute_scripts(esp, efuses, args): 505 efuses.batch_mode_cnt += 1 506 del args.operation 507 scripts = args.scripts 508 del args.scripts 509 510 for file in scripts: 511 with open(file.name, "r") as file: 512 exec(compile(file.read(), file.name, "exec")) 513 514 if args.debug: 515 for block in efuses.blocks: 516 data = block.get_bitstring(from_read=False) 517 block.print_block(data, "regs_for_burn", args.debug) 518 519 efuses.batch_mode_cnt -= 1 520 if not efuses.burn_all(check_batch_mode=True): 521 return 522 print("Successful") 523