1# This file describes eFuses for ESP32S2 chip 2# 3# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD 4# 5# SPDX-License-Identifier: GPL-2.0-or-later 6 7import binascii 8import struct 9import sys 10import time 11 12from bitstring import BitArray 13 14import esptool 15 16import reedsolo 17 18from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters 19from .. import base_fields 20from .. import util 21 22 23class EfuseBlock(base_fields.EfuseBlockBase): 24 def len_of_burn_unit(self): 25 # The writing register window is 8 registers for any blocks. 26 # len in bytes 27 return 8 * 4 28 29 def __init__(self, parent, param, skip_read=False): 30 parent.read_coding_scheme() 31 super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) 32 33 def apply_coding_scheme(self): 34 data = self.get_raw(from_read=False)[::-1] 35 if len(data) < self.len_of_burn_unit(): 36 add_empty_bytes = self.len_of_burn_unit() - len(data) 37 data = data + (b"\x00" * add_empty_bytes) 38 if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: 39 # takes 32 bytes 40 # apply RS encoding 41 rs = reedsolo.RSCodec(12) 42 # 32 byte of data + 12 bytes RS 43 encoded_data = rs.encode([x for x in data]) 44 words = struct.unpack("<" + "I" * 11, encoded_data) 45 # returns 11 words (8 words of data + 3 words of RS coding) 46 else: 47 # takes 32 bytes 48 words = struct.unpack("<" + ("I" * (len(data) // 4)), data) 49 # returns 8 words 50 return words 51 52 53class EspEfuses(base_fields.EspEfusesBase): 54 """ 55 Wrapper object to manage the efuse fields in a connected ESP bootloader 56 """ 57 58 debug = False 59 do_not_confirm = False 60 61 def __init__( 62 self, 63 esp, 64 skip_connect=False, 65 debug=False, 66 do_not_confirm=False, 67 extend_efuse_table=None, 68 ): 69 self.Blocks = EfuseDefineBlocks() 70 self.Fields = EfuseDefineFields(extend_efuse_table) 71 self.REGS = EfuseDefineRegisters 72 self.BURN_BLOCK_DATA_NAMES = self.Blocks.get_burn_block_data_names() 73 self.BLOCKS_FOR_KEYS = self.Blocks.get_blocks_for_keys() 74 self._esp = esp 75 self.debug = debug 76 self.do_not_confirm = do_not_confirm 77 if esp.CHIP_NAME != "ESP32-S2": 78 raise esptool.FatalError( 79 "Expected the 'esp' param for ESP32-S2 chip but got for '%s'." 80 % (esp.CHIP_NAME) 81 ) 82 if not skip_connect: 83 flags = self._esp.get_security_info()["flags"] 84 GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 85 if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: 86 raise esptool.FatalError( 87 "Secure Download Mode is enabled. The tool can not read eFuses." 88 ) 89 self.blocks = [ 90 EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) 91 for block in self.Blocks.BLOCKS 92 ] 93 if not skip_connect: 94 self.get_coding_scheme_warnings() 95 self.efuses = [EfuseField.convert(self, efuse) for efuse in self.Fields.EFUSES] 96 self.efuses += [ 97 EfuseField.convert(self, efuse) for efuse in self.Fields.KEYBLOCKS 98 ] 99 if skip_connect: 100 self.efuses += [ 101 EfuseField.convert(self, efuse) 102 for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES 103 ] 104 else: 105 if self["BLK_VERSION_MINOR"].get() == 1: 106 self.efuses += [ 107 EfuseField.convert(self, efuse) 108 for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES 109 ] 110 self.efuses += [ 111 EfuseField.convert(self, efuse) for efuse in self.Fields.CALC 112 ] 113 114 def __getitem__(self, efuse_name): 115 """Return the efuse field with the given name""" 116 for e in self.efuses: 117 if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): 118 return e 119 new_fields = False 120 for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: 121 if efuse.name == efuse_name or any( 122 x == efuse_name for x in efuse.alt_names 123 ): 124 self.efuses += [ 125 EfuseField.convert(self, efuse) 126 for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES 127 ] 128 new_fields = True 129 if new_fields: 130 for e in self.efuses: 131 if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): 132 return e 133 raise KeyError 134 135 def read_coding_scheme(self): 136 self.coding_scheme = self.REGS.CODING_SCHEME_RS 137 138 def print_status_regs(self): 139 print("") 140 self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) 141 print( 142 "{:27} 0x{:08x}".format( 143 "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) 144 ) 145 ) 146 print( 147 "{:27} 0x{:08x}".format( 148 "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) 149 ) 150 ) 151 152 def efuse_controller_setup(self): 153 self.set_efuse_timing() 154 self.clear_pgm_registers() 155 self.wait_efuse_idle() 156 157 def write_efuses(self, block): 158 self.efuse_program(block) 159 return self.get_coding_scheme_warnings(silent=True) 160 161 def clear_pgm_registers(self): 162 self.wait_efuse_idle() 163 for r in range( 164 self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 165 ): 166 self.write_reg(r, 0) 167 168 def wait_efuse_idle(self): 169 deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT 170 while time.time() < deadline: 171 cmds = self.REGS.EFUSE_PGM_CMD | self.REGS.EFUSE_READ_CMD 172 if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: 173 if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: 174 # Due to a hardware error, we have to read READ_CMD again 175 # to make sure the efuse clock is normal. 176 # For PGM_CMD it is not necessary. 177 return 178 raise esptool.FatalError( 179 "Timed out waiting for Efuse controller command to complete" 180 ) 181 182 def efuse_program(self, block): 183 self.wait_efuse_idle() 184 self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) 185 self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) 186 self.wait_efuse_idle() 187 self.clear_pgm_registers() 188 self.efuse_read() 189 190 def efuse_read(self): 191 self.wait_efuse_idle() 192 self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) 193 # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some 194 # efuse registers after each command is completed 195 # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. 196 try: 197 self.write_reg( 198 self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 199 ) 200 self.wait_efuse_idle() 201 except esptool.FatalError: 202 secure_download_mode_before = self._esp.secure_download_mode 203 204 try: 205 self._esp = self.reconnect_chip(self._esp) 206 except esptool.FatalError: 207 print("Can not re-connect to the chip") 208 if not self["DIS_DOWNLOAD_MODE"].get() and self[ 209 "DIS_DOWNLOAD_MODE" 210 ].get(from_read=False): 211 print( 212 "This is the correct behavior as we are actually burning " 213 "DIS_DOWNLOAD_MODE which disables the connection to the chip" 214 ) 215 print("DIS_DOWNLOAD_MODE is enabled") 216 print("Successful") 217 sys.exit(0) # finish without errors 218 raise 219 220 print("Established a connection with the chip") 221 if self._esp.secure_download_mode and not secure_download_mode_before: 222 print("Secure download mode is enabled") 223 if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ 224 "ENABLE_SECURITY_DOWNLOAD" 225 ].get(from_read=False): 226 print( 227 "espefuse tool can not continue to work in Secure download mode" 228 ) 229 print("ENABLE_SECURITY_DOWNLOAD is enabled") 230 print("Successful") 231 sys.exit(0) # finish without errors 232 raise 233 234 def set_efuse_timing(self): 235 """Set timing registers for burning efuses""" 236 # Configure clock 237 apb_freq = self.get_crystal_freq() 238 ( 239 EFUSE_TSUP_A, 240 EFUSE_TPGM, 241 EFUSE_THP_A, 242 EFUSE_TPGM_INACTIVE, 243 ) = self.REGS.EFUSE_PROGRAMMING_TIMING_PARAMETERS[apb_freq] 244 self.update_reg( 245 self.REGS.EFUSE_WR_TIM_CONF1_REG, self.REGS.EFUSE_TSUP_A_M, EFUSE_TSUP_A 246 ) 247 self.update_reg( 248 self.REGS.EFUSE_WR_TIM_CONF0_REG, self.REGS.EFUSE_TPGM_M, EFUSE_TPGM 249 ) 250 self.update_reg( 251 self.REGS.EFUSE_WR_TIM_CONF0_REG, self.REGS.EFUSE_THP_A_M, EFUSE_THP_A 252 ) 253 self.update_reg( 254 self.REGS.EFUSE_WR_TIM_CONF0_REG, 255 self.REGS.EFUSE_TPGM_INACTIVE_M, 256 EFUSE_TPGM_INACTIVE, 257 ) 258 259 ( 260 EFUSE_DAC_CLK_DIV, 261 EFUSE_PWR_ON_NUM, 262 EFUSE_PWR_OFF_NUM, 263 ) = self.REGS.VDDQ_TIMING_PARAMETERS[apb_freq] 264 self.update_reg( 265 self.REGS.EFUSE_DAC_CONF_REG, 266 self.REGS.EFUSE_DAC_CLK_DIV_M, 267 EFUSE_DAC_CLK_DIV, 268 ) 269 self.update_reg( 270 self.REGS.EFUSE_WR_TIM_CONF1_REG, 271 self.REGS.EFUSE_PWR_ON_NUM_M, 272 EFUSE_PWR_ON_NUM, 273 ) 274 self.update_reg( 275 self.REGS.EFUSE_WR_TIM_CONF2_REG, 276 self.REGS.EFUSE_PWR_OFF_NUM_M, 277 EFUSE_PWR_OFF_NUM, 278 ) 279 280 EFUSE_TSUR_A, EFUSE_TRD, EFUSE_THR_A = self.REGS.EFUSE_READING_PARAMETERS[ 281 apb_freq 282 ] 283 # self.update_reg( 284 # self.REGS.EFUSE_RD_TIM_CONF_REG, self.REGS.EFUSE_TSUR_A_M, EFUSE_TSUR_A 285 # ) 286 self.update_reg( 287 self.REGS.EFUSE_RD_TIM_CONF_REG, self.REGS.EFUSE_TRD_M, EFUSE_TRD 288 ) 289 self.update_reg( 290 self.REGS.EFUSE_RD_TIM_CONF_REG, self.REGS.EFUSE_THR_A_M, EFUSE_THR_A 291 ) 292 293 def get_coding_scheme_warnings(self, silent=False): 294 """Check if the coding scheme has detected any errors.""" 295 old_addr_reg = 0 296 reg_value = 0 297 ret_fail = False 298 for block in self.blocks: 299 if block.id == 0: 300 words = [ 301 self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) 302 for offs in range(5) 303 ] 304 block.err_bitarray.pos = 0 305 for word in reversed(words): 306 block.err_bitarray.overwrite(BitArray("uint:32=%d" % word)) 307 block.num_errors = block.err_bitarray.count(True) 308 block.fail = block.num_errors != 0 309 else: 310 addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ 311 block.id 312 ] 313 if err_num_mask is None or err_num_offs is None or fail_bit is None: 314 continue 315 if addr_reg != old_addr_reg: 316 old_addr_reg = addr_reg 317 reg_value = self.read_reg(addr_reg) 318 block.fail = reg_value & (1 << fail_bit) != 0 319 block.num_errors = (reg_value >> err_num_offs) & err_num_mask 320 ret_fail |= block.fail 321 if not silent and (block.fail or block.num_errors): 322 print( 323 "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" 324 % (block.id, block.num_errors, block.fail) 325 ) 326 if (self.debug or ret_fail) and not silent: 327 self.print_status_regs() 328 return ret_fail 329 330 def summary(self): 331 if self["VDD_SPI_FORCE"].get() == 0: 332 output = "Flash voltage (VDD_SPI) determined by GPIO45 on reset " 333 output += "(GPIO45=High: VDD_SPI pin is powered from internal 1.8V LDO\n" 334 output += "GPIO45=Low or NC: VDD_SPI pin is powered directly from " 335 output += "VDD3P3_RTC_IO via resistor Rspi. " 336 output += "Typically this voltage is 3.3 V)." 337 elif self["VDD_SPI_XPD"].get() == 0: 338 output = "Flash voltage (VDD_SPI) internal regulator disabled by efuse." 339 elif self["VDD_SPI_TIEH"].get() == 0: 340 output = "Flash voltage (VDD_SPI) set to 1.8V by efuse." 341 else: 342 output = "Flash voltage (VDD_SPI) set to 3.3V by efuse." 343 return output 344 345 346class EfuseField(base_fields.EfuseFieldBase): 347 @staticmethod 348 def convert(parent, efuse): 349 return { 350 "mac": EfuseMacField, 351 "keypurpose": EfuseKeyPurposeField, 352 "t_sensor": EfuseTempSensor, 353 "adc_tp": EfuseAdcPointCalibration, 354 "wafer": EfuseWafer, 355 }.get(efuse.class_type, EfuseField)(parent, efuse) 356 357 358class EfuseWafer(EfuseField): 359 def get(self, from_read=True): 360 hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) 361 assert self.parent["WAFER_VERSION_MINOR_HI"].bit_len == 1 362 lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) 363 assert self.parent["WAFER_VERSION_MINOR_LO"].bit_len == 3 364 return (hi_bits << 3) + lo_bits 365 366 def save(self, new_value): 367 raise esptool.FatalError("Burning %s is not supported" % self.name) 368 369 370class EfuseTempSensor(EfuseField): 371 def get(self, from_read=True): 372 value = self.get_bitstring(from_read) 373 sig = -1 if value[0] else 1 374 return sig * value[1:].uint * 0.1 375 376 377class EfuseAdcPointCalibration(EfuseField): 378 def get(self, from_read=True): 379 STEP_SIZE = 4 380 value = self.get_bitstring(from_read) 381 sig = -1 if value[0] else 1 382 return sig * value[1:].uint * STEP_SIZE 383 384 385class EfuseMacField(EfuseField): 386 def check_format(self, new_value_str): 387 if new_value_str is None: 388 raise esptool.FatalError( 389 "Required MAC Address in AA:CD:EF:01:02:03 format!" 390 ) 391 if new_value_str.count(":") != 5: 392 raise esptool.FatalError( 393 "MAC Address needs to be a 6-byte hexadecimal format " 394 "separated by colons (:)!" 395 ) 396 hexad = new_value_str.replace(":", "") 397 if len(hexad) != 12: 398 raise esptool.FatalError( 399 "MAC Address needs to be a 6-byte hexadecimal number " 400 "(12 hexadecimal characters)!" 401 ) 402 # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', 403 bindata = binascii.unhexlify(hexad) 404 # unicast address check according to 405 # https://tools.ietf.org/html/rfc7042#section-2.1 406 if esptool.util.byte(bindata, 0) & 0x01: 407 raise esptool.FatalError("Custom MAC must be a unicast MAC!") 408 return bindata 409 410 def check(self): 411 errs, fail = self.parent.get_block_errors(self.block) 412 if errs != 0 or fail: 413 output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) 414 else: 415 output = "OK" 416 return "(" + output + ")" 417 418 def get(self, from_read=True): 419 if self.name == "CUSTOM_MAC": 420 mac = self.get_raw(from_read)[::-1] 421 else: 422 mac = self.get_raw(from_read) 423 return "%s %s" % (util.hexify(mac, ":"), self.check()) 424 425 def save(self, new_value): 426 def print_field(e, new_value): 427 print( 428 " - '{}' ({}) {} -> {}".format( 429 e.name, e.description, e.get_bitstring(), new_value 430 ) 431 ) 432 433 if self.name == "CUSTOM_MAC": 434 bitarray_mac = self.convert_to_bitstring(new_value) 435 print_field(self, bitarray_mac) 436 super(EfuseMacField, self).save(new_value) 437 else: 438 # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, 439 # as it's written in the factory. 440 raise esptool.FatalError("Writing Factory MAC address is not supported") 441 442 443# fmt: off 444class EfuseKeyPurposeField(EfuseField): 445 KEY_PURPOSES = [ 446 ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) 447 ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved 448 ("XTS_AES_256_KEY_1", 2, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_1 (flash/PSRAM encryption) 449 ("XTS_AES_256_KEY_2", 3, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_2 (flash/PSRAM encryption) 450 ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) 451 ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode 452 ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) 453 ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) 454 ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode 455 ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) 456 ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) 457 ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) 458 ("XTS_AES_256_KEY", -1, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 459 ] 460# fmt: on 461 462 KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] 463 DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] 464 465 def check_format(self, new_value_str): 466 # str convert to int: "XTS_AES_128_KEY" - > str(4) 467 # if int: 4 -> str(4) 468 raw_val = new_value_str 469 for purpose_name in self.KEY_PURPOSES: 470 if purpose_name[0] == new_value_str: 471 raw_val = str(purpose_name[1]) 472 break 473 if raw_val.isdigit(): 474 if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: 475 raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) 476 else: 477 raise esptool.FatalError("'%s' unknown name" % raw_val) 478 return raw_val 479 480 def need_reverse(self, new_key_purpose): 481 for key in self.KEY_PURPOSES: 482 if key[0] == new_key_purpose: 483 return key[3] == "Reverse" 484 485 def need_rd_protect(self, new_key_purpose): 486 for key in self.KEY_PURPOSES: 487 if key[0] == new_key_purpose: 488 return key[4] == "need_rd_protect" 489 490 def get(self, from_read=True): 491 for p in self.KEY_PURPOSES: 492 if p[1] == self.get_raw(from_read): 493 return p[0] 494 return "FORBIDDEN_STATE" 495 496 def save(self, new_value): 497 raw_val = int(self.check_format(str(new_value))) 498 return super(EfuseKeyPurposeField, self).save(raw_val) 499