1# This file describes eFuses for ESP32-C2 chip 2# 3# SPDX-FileCopyrightText: 2021-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-C2": 78 raise esptool.FatalError( 79 "Expected the 'esp' param for ESP32-C2 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 111 def __getitem__(self, efuse_name): 112 """Return the efuse field with the given name""" 113 for e in self.efuses: 114 if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): 115 return e 116 new_fields = False 117 for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: 118 if efuse.name == efuse_name or any( 119 x == efuse_name for x in efuse.alt_names 120 ): 121 self.efuses += [ 122 EfuseField.convert(self, efuse) 123 for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES 124 ] 125 new_fields = True 126 if new_fields: 127 for e in self.efuses: 128 if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): 129 return e 130 raise KeyError 131 132 def read_coding_scheme(self): 133 self.coding_scheme = self.REGS.CODING_SCHEME_RS 134 135 def print_status_regs(self): 136 print("") 137 self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) 138 print( 139 "{:27} 0x{:08x}".format( 140 "EFUSE_RD_RS_ERR_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR_REG) 141 ) 142 ) 143 144 def efuse_controller_setup(self): 145 self.set_efuse_timing() 146 self.clear_pgm_registers() 147 self.wait_efuse_idle() 148 149 def write_efuses(self, block): 150 self.efuse_program(block) 151 return self.get_coding_scheme_warnings(silent=True) 152 153 def clear_pgm_registers(self): 154 self.wait_efuse_idle() 155 for r in range( 156 self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 157 ): 158 self.write_reg(r, 0) 159 160 def wait_efuse_idle(self): 161 deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT 162 while time.time() < deadline: 163 cmds = self.REGS.EFUSE_PGM_CMD | self.REGS.EFUSE_READ_CMD 164 if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: 165 if self.read_reg(self.REGS.EFUSE_CMD_REG) & cmds == 0: 166 # Due to a hardware error, we have to read READ_CMD again 167 # to make sure the efuse clock is normal. 168 # For PGM_CMD it is not necessary. 169 return 170 raise esptool.FatalError( 171 "Timed out waiting for Efuse controller command to complete" 172 ) 173 174 def efuse_program(self, block): 175 self.wait_efuse_idle() 176 self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) 177 self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) 178 self.wait_efuse_idle() 179 self.clear_pgm_registers() 180 self.efuse_read() 181 182 def efuse_read(self): 183 self.wait_efuse_idle() 184 self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) 185 # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some 186 # efuse registers after each command is completed 187 # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. 188 try: 189 self.write_reg( 190 self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 191 ) 192 self.wait_efuse_idle() 193 except esptool.FatalError: 194 secure_download_mode_before = self._esp.secure_download_mode 195 196 try: 197 self._esp = self.reconnect_chip(self._esp) 198 except esptool.FatalError: 199 print("Can not re-connect to the chip") 200 if not self["DIS_DOWNLOAD_MODE"].get() and self[ 201 "DIS_DOWNLOAD_MODE" 202 ].get(from_read=False): 203 print( 204 "This is the correct behavior as we are actually burning " 205 "DIS_DOWNLOAD_MODE which disables the connection to the chip" 206 ) 207 print("DIS_DOWNLOAD_MODE is enabled") 208 print("Successful") 209 sys.exit(0) # finish without errors 210 raise 211 212 print("Established a connection with the chip") 213 if self._esp.secure_download_mode and not secure_download_mode_before: 214 print("Secure download mode is enabled") 215 if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ 216 "ENABLE_SECURITY_DOWNLOAD" 217 ].get(from_read=False): 218 print( 219 "espefuse tool can not continue to work in Secure download mode" 220 ) 221 print("ENABLE_SECURITY_DOWNLOAD is enabled") 222 print("Successful") 223 sys.exit(0) # finish without errors 224 raise 225 226 def set_efuse_timing(self): 227 """Set timing registers for burning efuses""" 228 # Configure clock 229 xtal_freq = self.get_crystal_freq() 230 if xtal_freq not in [26, 40]: 231 raise esptool.FatalError( 232 "The eFuse supports only xtal=26M and 40M (xtal was %d)" % xtal_freq 233 ) 234 235 self.update_reg(self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_NUM_M, 0xFF) 236 self.update_reg( 237 self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_CLK_DIV_M, 0x28 238 ) 239 self.update_reg( 240 self.REGS.EFUSE_WR_TIM_CONF1_REG, self.REGS.EFUSE_PWR_ON_NUM_M, 0x3000 241 ) 242 self.update_reg( 243 self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 244 ) 245 246 tpgm_inactive_val = 200 if xtal_freq == 40 else 130 247 self.update_reg( 248 self.REGS.EFUSE_WR_TIM_CONF0_REG, 249 self.REGS.EFUSE_TPGM_INACTIVE_M, 250 tpgm_inactive_val, 251 ) 252 253 def get_coding_scheme_warnings(self, silent=False): 254 """Check if the coding scheme has detected any errors.""" 255 old_addr_reg = 0 256 reg_value = 0 257 ret_fail = False 258 for block in self.blocks: 259 if block.id == 0: 260 words = [ 261 self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR_REG + offs * 4) 262 for offs in range(1) 263 ] 264 block.err_bitarray.pos = 0 265 for word in reversed(words): 266 block.err_bitarray.overwrite(BitArray("uint:32=%d" % word)) 267 block.num_errors = block.err_bitarray.count(True) 268 block.fail = block.num_errors != 0 269 else: 270 addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ 271 block.id 272 ] 273 if err_num_mask is None or err_num_offs is None or fail_bit is None: 274 continue 275 if addr_reg != old_addr_reg: 276 old_addr_reg = addr_reg 277 reg_value = self.read_reg(addr_reg) 278 block.fail = reg_value & (1 << fail_bit) != 0 279 block.num_errors = (reg_value >> err_num_offs) & err_num_mask 280 ret_fail |= block.fail 281 if not silent and (block.fail or block.num_errors): 282 print( 283 "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" 284 % (block.id, block.num_errors, block.fail) 285 ) 286 if (self.debug or ret_fail) and not silent: 287 self.print_status_regs() 288 return ret_fail 289 290 def summary(self): 291 # TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)" 292 return "" 293 294 295class EfuseField(base_fields.EfuseFieldBase): 296 @staticmethod 297 def convert(parent, efuse): 298 return { 299 "mac": EfuseMacField, 300 "keypurpose": EfuseKeyPurposeField, 301 "t_sensor": EfuseTempSensor, 302 "adc_tp": EfuseAdcPointCalibration, 303 }.get(efuse.class_type, EfuseField)(parent, efuse) 304 305 306class EfuseTempSensor(EfuseField): 307 def get(self, from_read=True): 308 value = self.get_bitstring(from_read) 309 sig = -1 if value[0] else 1 310 return sig * value[1:].uint * 0.1 311 312 313class EfuseAdcPointCalibration(EfuseField): 314 def get(self, from_read=True): 315 STEP_SIZE = 4 316 value = self.get_bitstring(from_read) 317 sig = -1 if value[0] else 1 318 return sig * value[1:].uint * STEP_SIZE 319 320 321class EfuseMacField(EfuseField): 322 def check_format(self, new_value_str): 323 if new_value_str is None: 324 raise esptool.FatalError( 325 "Required MAC Address in AA:CD:EF:01:02:03 format!" 326 ) 327 if new_value_str.count(":") != 5: 328 raise esptool.FatalError( 329 "MAC Address needs to be a 6-byte hexadecimal format " 330 "separated by colons (:)!" 331 ) 332 hexad = new_value_str.replace(":", "") 333 if len(hexad) != 12: 334 raise esptool.FatalError( 335 "MAC Address needs to be a 6-byte hexadecimal number " 336 "(12 hexadecimal characters)!" 337 ) 338 # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', 339 bindata = binascii.unhexlify(hexad) 340 # unicast address check according to 341 # https://tools.ietf.org/html/rfc7042#section-2.1 342 if esptool.util.byte(bindata, 0) & 0x01: 343 raise esptool.FatalError("Custom MAC must be a unicast MAC!") 344 return bindata 345 346 def check(self): 347 errs, fail = self.parent.get_block_errors(self.block) 348 if errs != 0 or fail: 349 output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) 350 else: 351 output = "OK" 352 return "(" + output + ")" 353 354 def get(self, from_read=True): 355 if self.name == "CUSTOM_MAC": 356 mac = self.get_raw(from_read)[::-1] 357 else: 358 mac = self.get_raw(from_read) 359 return "%s %s" % (util.hexify(mac, ":"), self.check()) 360 361 def save(self, new_value): 362 def print_field(e, new_value): 363 print( 364 " - '{}' ({}) {} -> {}".format( 365 e.name, e.description, e.get_bitstring(), new_value 366 ) 367 ) 368 369 if self.name == "CUSTOM_MAC": 370 bitarray_mac = self.convert_to_bitstring(new_value) 371 print_field(self, bitarray_mac) 372 super(EfuseMacField, self).save(new_value) 373 else: 374 raise esptool.FatalError("Writing Factory MAC address is not supported") 375 376 377class EfuseKeyPurposeField(EfuseField): 378 # fmt: off 379 KEY_PURPOSES = [ 380 ("USER", 0, None), # User purposes (software-only use) 381 ("XTS_AES_128_KEY", 1, None), # (whole 256bits) flash/PSRAM encryption 382 ("XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS", 2, None), # (lo 128bits) flash/PSRAM encryption 383 ("SECURE_BOOT_DIGEST", 3, "DIGEST"), 384 # (hi 128bits) Secure Boot key digest 385 ] # fmt: on 386 387 KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] 388 DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] 389