1# This file includes the operations with eFuses for ESP32 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 os # noqa: F401. It is used in IDF scripts 9import traceback 10 11import espsecure 12 13import esptool 14 15from . import fields 16from .. import util 17from ..base_operations import ( 18 add_common_commands, 19 add_force_write_always, 20 add_show_sensitive_info_option, 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 add_commands(subparsers, efuses): 33 add_common_commands(subparsers, efuses) 34 p = subparsers.add_parser( 35 "burn_key", 36 help="Burn a 256-bit key to EFUSE: %s" % ", ".join(efuses.BLOCKS_FOR_KEYS), 37 ) 38 p.add_argument( 39 "--no-protect-key", 40 help="Disable default read- and write-protecting of the key. " 41 "If this option is not set, once the key is flashed " 42 "it cannot be read back or changed.", 43 action="store_true", 44 ) 45 add_force_write_always(p) 46 add_show_sensitive_info_option(p) 47 p.add_argument( 48 "block", 49 help='Key block to burn. "flash_encryption" (block1), ' 50 '"secure_boot_v1" (block2), "secure_boot_v2" (block2)', 51 action="append", 52 choices=efuses.BLOCKS_FOR_KEYS, 53 ) 54 p.add_argument( 55 "keyfile", 56 help="File containing 256 bits of binary key data", 57 action="append", 58 type=argparse.FileType("rb"), 59 ) 60 for _ in efuses.BLOCKS_FOR_KEYS: 61 p.add_argument( 62 "block", 63 help='Key block to burn. "flash_encryption" (block1), ' 64 '"secure_boot_v1" (block2), "secure_boot_v2" (block2)', 65 metavar="BLOCK", 66 nargs="?", 67 action="append", 68 choices=efuses.BLOCKS_FOR_KEYS, 69 ) 70 p.add_argument( 71 "keyfile", 72 help="File containing 256 bits of binary key data", 73 metavar="KEYFILE", 74 nargs="?", 75 action="append", 76 type=argparse.FileType("rb"), 77 ) 78 79 burn_key_digest = subparsers.add_parser( 80 "burn_key_digest", 81 help="Parse a RSA public key and burn the digest " 82 "to eFuse for use with Secure Boot V2", 83 ) 84 burn_key_digest.add_argument( 85 "keyfile", help="Key file to digest (PEM format)", type=argparse.FileType("rb") 86 ) 87 burn_key_digest.add_argument( 88 "--no-protect-key", 89 help="Disable default write-protecting of the key digest. " 90 "If this option is not set, once the key is flashed it cannot be changed.", 91 action="store_true", 92 ) 93 add_force_write_always(burn_key_digest) 94 add_show_sensitive_info_option(burn_key_digest) 95 96 p = subparsers.add_parser( 97 "set_flash_voltage", 98 help="Permanently set the internal flash voltage regulator " 99 "to either 1.8V, 3.3V or OFF. This means GPIO12 can be high or low at reset " 100 "without changing the flash voltage.", 101 ) 102 p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) 103 104 p = subparsers.add_parser( 105 "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." 106 ) 107 p.add_argument( 108 "mac", 109 help="Custom MAC Address to burn given in hexadecimal format " 110 "with bytes separated by colons " 111 "(e.g. AA:CD:EF:01:02:03).", 112 type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), 113 ) 114 add_force_write_always(p) 115 116 p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") 117 118 119def burn_custom_mac(esp, efuses, args): 120 # Writing to BLK3: 121 # - MAC_VERSION = 1 122 # - CUSTOM_MAC = AA:CD:EF:01:02:03 123 # - CUSTOM_MAC_CRC = crc8(CUSTOM_MAC) 124 efuses["CUSTOM_MAC"].save(args.mac) 125 if not efuses.burn_all(check_batch_mode=True): 126 return 127 get_custom_mac(esp, efuses, args) 128 print("Successful") 129 130 131def get_custom_mac(esp, efuses, args): 132 version = efuses["MAC_VERSION"].get() 133 if version > 0: 134 print( 135 "Custom MAC Address version {}: {}".format( 136 version, efuses["CUSTOM_MAC"].get() 137 ) 138 ) 139 else: 140 print("Custom MAC Address is not set in the device.") 141 142 143def set_flash_voltage(esp, efuses, args): 144 sdio_force = efuses["XPD_SDIO_FORCE"] 145 sdio_tieh = efuses["XPD_SDIO_TIEH"] 146 sdio_reg = efuses["XPD_SDIO_REG"] 147 148 # check efuses aren't burned in a way which makes this impossible 149 if args.voltage == "OFF" and sdio_reg.get() != 0: 150 raise esptool.FatalError( 151 "Can't set flash regulator to OFF as XPD_SDIO_REG efuse is already burned" 152 ) 153 154 if args.voltage == "1.8V" and sdio_tieh.get() != 0: 155 raise esptool.FatalError( 156 "Can't set regulator to 1.8V is XPD_SDIO_TIEH efuse is already burned" 157 ) 158 159 if args.voltage == "OFF": 160 msg = "Disable internal flash voltage regulator (VDD_SDIO). " 161 "SPI flash will need to be powered from an external source.\n" 162 "The following efuse is burned: XPD_SDIO_FORCE.\n" 163 "It is possible to later re-enable the internal regulator (%s) " % ( 164 "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" 165 ) 166 "by burning an additional efuse" 167 elif args.voltage == "1.8V": 168 msg = "Set internal flash voltage regulator (VDD_SDIO) to 1.8V.\n" 169 "The following efuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG.\n" 170 "It is possible to later increase the voltage to 3.3V (permanently) " 171 "by burning additional efuse XPD_SDIO_TIEH" 172 elif args.voltage == "3.3V": 173 msg = "Enable internal flash voltage regulator (VDD_SDIO) to 3.3V.\n" 174 "The following efuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG, XPD_SDIO_TIEH." 175 print(msg) 176 sdio_force.save(1) # Disable GPIO12 177 if args.voltage != "OFF": 178 sdio_reg.save(1) # Enable internal regulator 179 if args.voltage == "3.3V": 180 sdio_tieh.save(1) 181 print("VDD_SDIO setting complete.") 182 if not efuses.burn_all(check_batch_mode=True): 183 return 184 print("Successful") 185 186 187def adc_info(esp, efuses, args): 188 adc_vref = efuses["ADC_VREF"] 189 blk3_reserve = efuses["BLK3_PART_RESERVE"] 190 191 vref_raw = adc_vref.get_raw() 192 if vref_raw == 0: 193 print("ADC VRef calibration: None (1100mV nominal)") 194 else: 195 print("ADC VRef calibration: %dmV" % adc_vref.get()) 196 197 if blk3_reserve.get(): 198 print("ADC readings stored in efuse BLOCK3:") 199 print(" ADC1 Low reading (150mV): %d" % efuses["ADC1_TP_LOW"].get()) 200 print(" ADC1 High reading (850mV): %d" % efuses["ADC1_TP_HIGH"].get()) 201 print(" ADC2 Low reading (150mV): %d" % efuses["ADC2_TP_LOW"].get()) 202 print(" ADC2 High reading (850mV): %d" % efuses["ADC2_TP_HIGH"].get()) 203 204 205def burn_key(esp, efuses, args): 206 datafile_list = args.keyfile[ 207 0 : len([keyfile for keyfile in args.keyfile if keyfile is not None]) : 208 ] 209 block_name_list = args.block[ 210 0 : len([block for block in args.block if block is not None]) : 211 ] 212 efuses.force_write_always = args.force_write_always 213 no_protect_key = args.no_protect_key 214 215 util.check_duplicate_name_in_list(block_name_list) 216 if len(block_name_list) != len(datafile_list): 217 raise esptool.FatalError( 218 "The number of blocks (%d) and datafile (%d) should be the same." 219 % (len(block_name_list), len(datafile_list)) 220 ) 221 222 print("Burn keys to blocks:") 223 for block_name, datafile in zip(block_name_list, datafile_list): 224 efuse = None 225 for block in efuses.blocks: 226 if block_name == block.name or block_name in block.alias: 227 efuse = efuses[block.name] 228 if efuse is None: 229 raise esptool.FatalError("Unknown block name - %s" % (block_name)) 230 num_bytes = efuse.bit_len // 8 231 data = datafile.read() 232 revers_msg = None 233 if block_name in ("flash_encryption", "secure_boot_v1"): 234 revers_msg = "\tReversing the byte order" 235 data = data[::-1] 236 print(" - %s" % (efuse.name), end=" ") 237 print( 238 "-> [{}]".format( 239 util.hexify(data, " ") 240 if args.show_sensitive_info 241 else " ".join(["??"] * len(data)) 242 ) 243 ) 244 if revers_msg: 245 print(revers_msg) 246 if len(data) != num_bytes: 247 raise esptool.FatalError( 248 "Incorrect key file size %d. " 249 "Key file must be %d bytes (%d bits) of raw binary key data." 250 % (len(data), num_bytes, num_bytes * 8) 251 ) 252 253 efuse.save(data) 254 255 if block_name in ("flash_encryption", "secure_boot_v1"): 256 if not no_protect_key: 257 print("\tDisabling read to key block") 258 efuse.disable_read() 259 260 if not no_protect_key: 261 print("\tDisabling write to key block") 262 efuse.disable_write() 263 print("") 264 265 if args.no_protect_key: 266 print("Key is left unprotected as per --no-protect-key argument.") 267 268 msg = "Burn keys in efuse blocks.\n" 269 if no_protect_key: 270 msg += ( 271 "The key block will left readable and writeable (due to --no-protect-key)" 272 ) 273 else: 274 msg += "The key block will be read and write protected " 275 "(no further changes or readback)" 276 print(msg, "\n") 277 if not efuses.burn_all(check_batch_mode=True): 278 return 279 print("Successful") 280 281 282def burn_key_digest(esp, efuses, args): 283 if efuses.coding_scheme == efuses.REGS.CODING_SCHEME_34: 284 raise esptool.FatalError("burn_key_digest only works with 'None' coding scheme") 285 286 chip_revision = esp.get_chip_revision() 287 if chip_revision < 300: 288 raise esptool.FatalError( 289 "Incorrect chip revision for Secure boot v2. " 290 "Detected: v%d.%d. Expected: >= v3.0" 291 % (chip_revision / 100, chip_revision % 100) 292 ) 293 294 digest = espsecure._digest_sbv2_public_key(args.keyfile) 295 efuse = efuses["BLOCK2"] 296 num_bytes = efuse.bit_len // 8 297 if len(digest) != num_bytes: 298 raise esptool.FatalError( 299 "Incorrect digest size %d. " 300 "Digest must be %d bytes (%d bits) of raw binary key data." 301 % (len(digest), num_bytes, num_bytes * 8) 302 ) 303 print(" - %s" % (efuse.name), end=" ") 304 print( 305 "-> [{}]".format( 306 util.hexify(digest, " ") 307 if args.show_sensitive_info 308 else " ".join(["??"] * len(digest)) 309 ) 310 ) 311 312 efuse.save(digest) 313 if not args.no_protect_key: 314 print("Disabling write to efuse %s..." % (efuse.name)) 315 efuse.disable_write() 316 317 if not efuses.burn_all(check_batch_mode=True): 318 return 319 print("Successful") 320 321 322def espefuse(esp, efuses, args, command): 323 parser = argparse.ArgumentParser() 324 subparsers = parser.add_subparsers(dest="operation") 325 add_commands(subparsers, efuses) 326 try: 327 cmd_line_args = parser.parse_args(command.split()) 328 except SystemExit: 329 traceback.print_stack() 330 raise esptool.FatalError('"{}" - incorrect command'.format(command)) 331 if cmd_line_args.operation == "execute_scripts": 332 configfiles = cmd_line_args.configfiles 333 index = cmd_line_args.index 334 # copy arguments from args to cmd_line_args 335 vars(cmd_line_args).update(vars(args)) 336 if cmd_line_args.operation == "execute_scripts": 337 cmd_line_args.configfiles = configfiles 338 cmd_line_args.index = index 339 if cmd_line_args.operation is None: 340 parser.print_help() 341 parser.exit(1) 342 operation_func = globals()[cmd_line_args.operation] 343 # each 'operation' is a module-level function of the same name 344 operation_func(esp, efuses, cmd_line_args) 345 346 347def execute_scripts(esp, efuses, args): 348 efuses.batch_mode_cnt += 1 349 del args.operation 350 scripts = args.scripts 351 del args.scripts 352 353 for file in scripts: 354 with open(file.name, "r") as file: 355 exec(compile(file.read(), file.name, "exec")) 356 357 if args.debug: 358 for block in efuses.blocks: 359 data = block.get_bitstring(from_read=False) 360 block.print_block(data, "regs_for_burn", args.debug) 361 362 efuses.batch_mode_cnt -= 1 363 if not efuses.burn_all(check_batch_mode=True): 364 return 365 print("Successful") 366