1# This file includes the operations with eFuses for ESP32-C6 chip 2# 3# SPDX-FileCopyrightText: 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 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 " 37 "post-write 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. For the rest keypurposes the read-protection " 47 "will be defined the option (Read-protect by default).", 48 action="store_true", 49 ) 50 51 52def add_commands(subparsers, efuses): 53 add_common_commands(subparsers, efuses) 54 burn_key = subparsers.add_parser( 55 "burn_key", help="Burn the key block with the specified name" 56 ) 57 protect_options(burn_key) 58 add_force_write_always(burn_key) 59 add_show_sensitive_info_option(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 add_show_sensitive_info_option(burn_key_digest) 111 burn_key_digest.add_argument( 112 "block", 113 help="Key block to burn", 114 action="append", 115 choices=efuses.BLOCKS_FOR_KEYS, 116 ) 117 burn_key_digest.add_argument( 118 "keyfile", 119 help="Key file to digest (PEM format)", 120 action="append", 121 type=argparse.FileType("rb"), 122 ) 123 burn_key_digest.add_argument( 124 "keypurpose", 125 help="Purpose to set.", 126 action="append", 127 choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, 128 ) 129 for _ in efuses.BLOCKS_FOR_KEYS: 130 burn_key_digest.add_argument( 131 "block", 132 help="Key block to burn", 133 nargs="?", 134 action="append", 135 metavar="BLOCK", 136 choices=efuses.BLOCKS_FOR_KEYS, 137 ) 138 burn_key_digest.add_argument( 139 "keyfile", 140 help="Key file to digest (PEM format)", 141 nargs="?", 142 action="append", 143 metavar="KEYFILE", 144 type=argparse.FileType("rb"), 145 ) 146 burn_key_digest.add_argument( 147 "keypurpose", 148 help="Purpose to set.", 149 nargs="?", 150 action="append", 151 metavar="KEYPURPOSE", 152 choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, 153 ) 154 155 p = subparsers.add_parser( 156 "set_flash_voltage", 157 help="Permanently set the internal flash voltage regulator " 158 "to either 1.8V, 3.3V or OFF. " 159 "This means GPIO45 can be high or low at reset without " 160 "changing the flash voltage.", 161 ) 162 p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) 163 164 p = subparsers.add_parser( 165 "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." 166 ) 167 p.add_argument( 168 "mac", 169 help="Custom MAC Address to burn given in hexadecimal format with bytes " 170 "separated by colons (e.g. AA:CD:EF:01:02:03).", 171 type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), 172 ) 173 add_force_write_always(p) 174 175 p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") 176 177 178def burn_custom_mac(esp, efuses, args): 179 efuses["CUSTOM_MAC"].save(args.mac) 180 if not efuses.burn_all(check_batch_mode=True): 181 return 182 get_custom_mac(esp, efuses, args) 183 print("Successful") 184 185 186def get_custom_mac(esp, efuses, args): 187 print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) 188 189 190def set_flash_voltage(esp, efuses, args): 191 raise esptool.FatalError("set_flash_voltage is not supported!") 192 193 194def adc_info(esp, efuses, args): 195 print("") 196 # fmt: off 197 if efuses["BLK_VERSION_MINOR"].get() == 1: 198 print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_CALIB"].get())) 199 print("ADC OCode = ", efuses["OCODE"].get()) 200 print("ADC1:") 201 print("INIT_CODE_ATTEN0 = ", efuses['ADC1_INIT_CODE_ATTEN0'].get()) 202 print("INIT_CODE_ATTEN1 = ", efuses['ADC1_INIT_CODE_ATTEN1'].get()) 203 print("INIT_CODE_ATTEN2 = ", efuses['ADC1_INIT_CODE_ATTEN2'].get()) 204 print("INIT_CODE_ATTEN3 = ", efuses['ADC1_INIT_CODE_ATTEN3'].get()) 205 print("CAL_VOL_ATTEN0 = ", efuses['ADC1_CAL_VOL_ATTEN0'].get()) 206 print("CAL_VOL_ATTEN1 = ", efuses['ADC1_CAL_VOL_ATTEN1'].get()) 207 print("CAL_VOL_ATTEN2 = ", efuses['ADC1_CAL_VOL_ATTEN2'].get()) 208 print("CAL_VOL_ATTEN3 = ", efuses['ADC1_CAL_VOL_ATTEN3'].get()) 209 print("INIT_CODE_ATTEN0_CH0 = ", efuses['ADC1_INIT_CODE_ATTEN0_CH0'].get()) 210 print("INIT_CODE_ATTEN0_CH1 = ", efuses['ADC1_INIT_CODE_ATTEN0_CH1'].get()) 211 print("INIT_CODE_ATTEN0_CH2 = ", efuses['ADC1_INIT_CODE_ATTEN0_CH2'].get()) 212 print("INIT_CODE_ATTEN0_CH3 = ", efuses['ADC1_INIT_CODE_ATTEN0_CH3'].get()) 213 print("INIT_CODE_ATTEN0_CH4 = ", efuses['ADC1_INIT_CODE_ATTEN0_CH4'].get()) 214 print("INIT_CODE_ATTEN0_CH5 = ", efuses['ADC1_INIT_CODE_ATTEN0_CH5'].get()) 215 print("INIT_CODE_ATTEN0_CH6 = ", efuses['ADC1_INIT_CODE_ATTEN0_CH6'].get()) 216 else: 217 print("BLK_VERSION_MINOR = {}".format(efuses["BLK_VERSION_MINOR"].get_meaning())) 218 # fmt: on 219 220 221def burn_key(esp, efuses, args, digest=None): 222 if digest is None: 223 datafile_list = args.keyfile[ 224 0 : len([name for name in args.keyfile if name is not None]) : 225 ] 226 else: 227 datafile_list = digest[0 : len([name for name in digest if name is not None]) :] 228 efuses.force_write_always = args.force_write_always 229 block_name_list = args.block[ 230 0 : len([name for name in args.block if name is not None]) : 231 ] 232 keypurpose_list = args.keypurpose[ 233 0 : len([name for name in args.keypurpose if name is not None]) : 234 ] 235 236 util.check_duplicate_name_in_list(block_name_list) 237 if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( 238 keypurpose_list 239 ): 240 raise esptool.FatalError( 241 "The number of blocks (%d), datafile (%d) and keypurpose (%d) " 242 "should be the same." 243 % (len(block_name_list), len(datafile_list), len(keypurpose_list)) 244 ) 245 246 print("Burn keys to blocks:") 247 for block_name, datafile, keypurpose in zip( 248 block_name_list, datafile_list, keypurpose_list 249 ): 250 efuse = None 251 for block in efuses.blocks: 252 if block_name == block.name or block_name in block.alias: 253 efuse = efuses[block.name] 254 if efuse is None: 255 raise esptool.FatalError("Unknown block name - %s" % (block_name)) 256 num_bytes = efuse.bit_len // 8 257 258 block_num = efuses.get_index_block_by_name(block_name) 259 block = efuses.blocks[block_num] 260 261 if digest is None: 262 data = datafile.read() 263 else: 264 data = datafile 265 266 print(" - %s" % (efuse.name), end=" ") 267 revers_msg = None 268 if efuses[block.key_purpose_name].need_reverse(keypurpose): 269 revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" 270 data = data[::-1] 271 print( 272 "-> [{}]".format( 273 util.hexify(data, " ") 274 if args.show_sensitive_info 275 else " ".join(["??"] * len(data)) 276 ) 277 ) 278 if revers_msg: 279 print(revers_msg) 280 if len(data) != num_bytes: 281 raise esptool.FatalError( 282 "Incorrect key file size %d. Key file must be %d bytes (%d bits) " 283 "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) 284 ) 285 286 if efuses[block.key_purpose_name].need_rd_protect(keypurpose): 287 read_protect = False if args.no_read_protect else True 288 else: 289 read_protect = False 290 write_protect = not args.no_write_protect 291 292 # using efuse instead of a block gives the advantage of checking it as the whole field. 293 efuse.save(data) 294 295 disable_wr_protect_key_purpose = False 296 if efuses[block.key_purpose_name].get() != keypurpose: 297 if efuses[block.key_purpose_name].is_writeable(): 298 print( 299 "\t'%s': '%s' -> '%s'." 300 % ( 301 block.key_purpose_name, 302 efuses[block.key_purpose_name].get(), 303 keypurpose, 304 ) 305 ) 306 efuses[block.key_purpose_name].save(keypurpose) 307 disable_wr_protect_key_purpose = True 308 else: 309 raise esptool.FatalError( 310 "It is not possible to change '%s' to '%s' " 311 "because write protection bit is set." 312 % (block.key_purpose_name, keypurpose) 313 ) 314 else: 315 print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) 316 if efuses[block.key_purpose_name].is_writeable(): 317 disable_wr_protect_key_purpose = True 318 319 if disable_wr_protect_key_purpose: 320 print("\tDisabling write to '%s'." % block.key_purpose_name) 321 efuses[block.key_purpose_name].disable_write() 322 323 if read_protect: 324 print("\tDisabling read to key block") 325 efuse.disable_read() 326 327 if write_protect: 328 print("\tDisabling write to key block") 329 efuse.disable_write() 330 print("") 331 332 if not write_protect: 333 print("Keys will remain writeable (due to --no-write-protect)") 334 if args.no_read_protect: 335 print("Keys will remain readable (due to --no-read-protect)") 336 337 if not efuses.burn_all(check_batch_mode=True): 338 return 339 print("Successful") 340 341 342def burn_key_digest(esp, efuses, args): 343 digest_list = [] 344 datafile_list = args.keyfile[ 345 0 : len([name for name in args.keyfile if name is not None]) : 346 ] 347 block_list = args.block[ 348 0 : len([block for block in args.block if block is not None]) : 349 ] 350 for block_name, datafile in zip(block_list, datafile_list): 351 efuse = None 352 for block in efuses.blocks: 353 if block_name == block.name or block_name in block.alias: 354 efuse = efuses[block.name] 355 if efuse is None: 356 raise esptool.FatalError("Unknown block name - %s" % (block_name)) 357 num_bytes = efuse.bit_len // 8 358 digest = espsecure._digest_sbv2_public_key(datafile) 359 if len(digest) != num_bytes: 360 raise esptool.FatalError( 361 "Incorrect digest size %d. Digest must be %d bytes (%d bits) " 362 "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) 363 ) 364 digest_list.append(digest) 365 burn_key(esp, efuses, args, digest=digest_list) 366 367 368def espefuse(esp, efuses, args, command): 369 parser = argparse.ArgumentParser() 370 subparsers = parser.add_subparsers(dest="operation") 371 add_commands(subparsers, efuses) 372 try: 373 cmd_line_args = parser.parse_args(command.split()) 374 except SystemExit: 375 traceback.print_stack() 376 raise esptool.FatalError('"{}" - incorrect command'.format(command)) 377 if cmd_line_args.operation == "execute_scripts": 378 configfiles = cmd_line_args.configfiles 379 index = cmd_line_args.index 380 # copy arguments from args to cmd_line_args 381 vars(cmd_line_args).update(vars(args)) 382 if cmd_line_args.operation == "execute_scripts": 383 cmd_line_args.configfiles = configfiles 384 cmd_line_args.index = index 385 if cmd_line_args.operation is None: 386 parser.print_help() 387 parser.exit(1) 388 operation_func = globals()[cmd_line_args.operation] 389 # each 'operation' is a module-level function of the same name 390 operation_func(esp, efuses, cmd_line_args) 391 392 393def execute_scripts(esp, efuses, args): 394 efuses.batch_mode_cnt += 1 395 del args.operation 396 scripts = args.scripts 397 del args.scripts 398 399 for file in scripts: 400 with open(file.name, "r") as file: 401 exec(compile(file.read(), file.name, "exec")) 402 403 if args.debug: 404 for block in efuses.blocks: 405 data = block.get_bitstring(from_read=False) 406 block.print_block(data, "regs_for_burn", args.debug) 407 408 efuses.batch_mode_cnt -= 1 409 if not efuses.burn_all(check_batch_mode=True): 410 return 411 print("Successful") 412