1# This file describes 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 binascii 8import struct 9import time 10 11import esptool 12 13from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters 14from .. import base_fields 15from .. import util 16 17 18class EfuseBlock(base_fields.EfuseBlockBase): 19 def len_of_burn_unit(self): 20 # The writing register window is the same as len of a block. 21 return self.len 22 23 def __init__(self, parent, param, skip_read=False): 24 if skip_read: 25 parent.coding_scheme = parent.REGS.CODING_SCHEME_NONE 26 else: 27 if parent.coding_scheme is None: 28 parent.read_coding_scheme() 29 super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) 30 31 def apply_coding_scheme(self): 32 data = self.get_raw(from_read=False)[::-1] 33 if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_34: 34 # CODING_SCHEME 3/4 applied only for BLK1..3 35 # Takes 24 byte sequence to be represented in 3/4 encoding, 36 # returns 8 words suitable for writing "encoded" to an efuse block 37 if len(data) != 24: 38 raise esptool.FatalError("Should take 24 bytes for 3/4 encoding.") 39 data = data[:24] 40 outbits = b"" 41 while len(data) > 0: # process in chunks of 6 bytes 42 bits = data[0:6] 43 data = data[6:] 44 xor_res = 0 45 mul_res = 0 46 index = 1 47 for b in struct.unpack("B" * 6, bits): 48 xor_res ^= b 49 mul_res += index * util.popcnt(b) 50 index += 1 51 outbits += bits 52 outbits += struct.pack("BB", xor_res, mul_res) 53 words = struct.unpack("<" + "I" * (len(outbits) // 4), outbits) 54 # returns 8 words 55 else: 56 # CODING_SCHEME NONE applied for BLK0 and BLK1..3 57 # BLK0 len = 7 words, BLK1..3 len = 8 words. 58 words = struct.unpack("<" + ("I" * (len(data) // 4)), data) 59 # returns 7 words for BLK0 or 8 words for BLK1..3 60 return words 61 62 63class EspEfuses(base_fields.EspEfusesBase): 64 """ 65 Wrapper object to manage the efuse fields in a connected ESP bootloader 66 """ 67 68 debug = False 69 do_not_confirm = False 70 71 def __init__( 72 self, 73 esp, 74 skip_connect=False, 75 debug=False, 76 do_not_confirm=False, 77 extend_efuse_table=None, 78 ): 79 self.Blocks = EfuseDefineBlocks() 80 self.Fields = EfuseDefineFields(extend_efuse_table) 81 self.REGS = EfuseDefineRegisters 82 self.BURN_BLOCK_DATA_NAMES = self.Blocks.get_burn_block_data_names() 83 self.BLOCKS_FOR_KEYS = self.Blocks.get_blocks_for_keys() 84 self._esp = esp 85 self.debug = debug 86 self.do_not_confirm = do_not_confirm 87 if esp.CHIP_NAME != "ESP32": 88 raise esptool.FatalError( 89 "Expected the 'esp' param for ESP32 chip but got for '%s'." 90 % (esp.CHIP_NAME) 91 ) 92 self.blocks = [ 93 EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) 94 for block in self.Blocks.BLOCKS 95 ] 96 if not skip_connect: 97 self.get_coding_scheme_warnings() 98 self.efuses = [EfuseField.convert(self, efuse) for efuse in self.Fields.EFUSES] 99 if skip_connect: 100 self.efuses += [ 101 EfuseField.convert(self, efuse) for efuse in self.Fields.KEYBLOCKS_256 102 ] 103 self.efuses += [ 104 EfuseField.convert(self, efuse) for efuse in self.Fields.CUSTOM_MAC 105 ] 106 self.efuses += [ 107 EfuseField.convert(self, efuse) for efuse in self.Fields.ADC_CALIBRATION 108 ] 109 else: 110 if self.coding_scheme == self.REGS.CODING_SCHEME_NONE: 111 self.efuses += [ 112 EfuseField.convert(self, efuse) 113 for efuse in self.Fields.KEYBLOCKS_256 114 ] 115 elif self.coding_scheme == self.REGS.CODING_SCHEME_34: 116 self.efuses += [ 117 EfuseField.convert(self, efuse) 118 for efuse in self.Fields.KEYBLOCKS_192 119 ] 120 else: 121 raise esptool.FatalError( 122 "The coding scheme (%d) - is not supported" % self.coding_scheme 123 ) 124 if self["MAC_VERSION"].get() == 1: 125 self.efuses += [ 126 EfuseField.convert(self, efuse) for efuse in self.Fields.CUSTOM_MAC 127 ] 128 if self["BLK3_PART_RESERVE"].get(): 129 self.efuses += [ 130 EfuseField.convert(self, efuse) 131 for efuse in self.Fields.ADC_CALIBRATION 132 ] 133 self.efuses += [ 134 EfuseField.convert(self, efuse) for efuse in self.Fields.CALC 135 ] 136 137 def __getitem__(self, efuse_name): 138 """Return the efuse field with the given name""" 139 for e in self.efuses: 140 if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): 141 return e 142 new_fields = False 143 for efuse in self.Fields.CUSTOM_MAC: 144 if efuse.name == efuse_name or any( 145 x == efuse_name for x in efuse.alt_names 146 ): 147 self.efuses += [ 148 EfuseField.convert(self, efuse) for efuse in self.Fields.CUSTOM_MAC 149 ] 150 new_fields = True 151 for efuse in self.Fields.ADC_CALIBRATION: 152 if efuse.name == efuse_name or any( 153 x == efuse_name for x in efuse.alt_names 154 ): 155 self.efuses += [ 156 EfuseField.convert(self, efuse) 157 for efuse in self.Fields.ADC_CALIBRATION 158 ] 159 new_fields = True 160 if new_fields: 161 for e in self.efuses: 162 if efuse_name == e.name or any(x == efuse_name for x in e.alt_names): 163 return e 164 raise KeyError 165 166 def read_coding_scheme(self): 167 coding_scheme = ( 168 self.read_efuse(self.REGS.EFUSE_CODING_SCHEME_WORD) 169 & self.REGS.EFUSE_CODING_SCHEME_MASK 170 ) 171 if coding_scheme == self.REGS.CODING_SCHEME_NONE_RECOVERY: 172 self.coding_scheme = self.REGS.CODING_SCHEME_NONE 173 else: 174 self.coding_scheme = coding_scheme 175 176 def print_status_regs(self): 177 print("") 178 print( 179 "{:27} 0x{:08x}".format( 180 "EFUSE_REG_DEC_STATUS", self.read_reg(self.REGS.EFUSE_REG_DEC_STATUS) 181 ) 182 ) 183 184 def write_efuses(self, block): 185 """Write the values in the efuse write registers to 186 the efuse hardware, then refresh the efuse read registers. 187 """ 188 189 # Configure clock 190 apb_freq = self.get_crystal_freq() 191 clk_sel0, clk_sel1, dac_clk_div = self.REGS.EFUSE_CLK_SETTINGS[apb_freq] 192 193 self.update_reg( 194 self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_CLK_DIV_MASK, dac_clk_div 195 ) 196 self.update_reg( 197 self.REGS.EFUSE_CLK_REG, self.REGS.EFUSE_CLK_SEL0_MASK, clk_sel0 198 ) 199 self.update_reg( 200 self.REGS.EFUSE_CLK_REG, self.REGS.EFUSE_CLK_SEL1_MASK, clk_sel1 201 ) 202 203 self.write_reg(self.REGS.EFUSE_REG_CONF, self.REGS.EFUSE_CONF_WRITE) 204 self.write_reg(self.REGS.EFUSE_REG_CMD, self.REGS.EFUSE_CMD_WRITE) 205 206 self.efuse_read() 207 return self.get_coding_scheme_warnings(silent=True) 208 209 def wait_efuse_idle(self): 210 deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT 211 while time.time() < deadline: 212 if self.read_reg(self.REGS.EFUSE_REG_CMD) == 0: 213 return 214 raise esptool.FatalError( 215 "Timed out waiting for Efuse controller command to complete" 216 ) 217 218 def efuse_read(self): 219 self.wait_efuse_idle() 220 self.write_reg(self.REGS.EFUSE_REG_CONF, self.REGS.EFUSE_CONF_READ) 221 self.write_reg(self.REGS.EFUSE_REG_CMD, self.REGS.EFUSE_CMD_READ) 222 self.wait_efuse_idle() 223 224 def get_coding_scheme_warnings(self, silent=False): 225 """Check if the coding scheme has detected any errors. 226 Meaningless for default coding scheme (0) 227 """ 228 err = ( 229 self.read_reg(self.REGS.EFUSE_REG_DEC_STATUS) 230 & self.REGS.EFUSE_REG_DEC_STATUS_MASK 231 ) 232 for block in self.blocks: 233 if block.id != 0: 234 block.num_errors = 0 235 block.fail = err != 0 236 if not silent and block.fail: 237 print( 238 "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" 239 % (block.id, block.num_errors, block.fail) 240 ) 241 if (self.debug or err) and not silent: 242 self.print_status_regs() 243 return err != 0 244 245 def summary(self): 246 if self["XPD_SDIO_FORCE"].get() == 0: 247 output = "Flash voltage (VDD_SDIO) determined by GPIO12 on reset (High for 1.8V, Low/NC for 3.3V)" 248 elif self["XPD_SDIO_REG"].get() == 0: 249 output = "Flash voltage (VDD_SDIO) internal regulator disabled by efuse." 250 elif self["XPD_SDIO_TIEH"].get() == 0: 251 output = "Flash voltage (VDD_SDIO) set to 1.8V by efuse." 252 else: 253 output = "Flash voltage (VDD_SDIO) set to 3.3V by efuse." 254 return output 255 256 257class EfuseField(base_fields.EfuseFieldBase): 258 @staticmethod 259 def convert(parent, efuse): 260 return { 261 "mac": EfuseMacField, 262 "spipin": EfuseSpiPinField, 263 "vref": EfuseVRefField, 264 "adc_tp": EfuseAdcPointCalibration, 265 "wafer": EfuseWafer, 266 "pkg": EfusePkg, 267 }.get(efuse.class_type, EfuseField)(parent, efuse) 268 269 270class EfuseMacField(EfuseField): 271 """ 272 Supports: MAC and CUSTOM_MAC fields. 273 (if MAC_VERSION == 1 then the CUSTOM_MAC is used) 274 """ 275 276 def check_format(self, new_value_str): 277 if new_value_str is None: 278 raise esptool.FatalError( 279 "Required MAC Address in AA:CD:EF:01:02:03 format!" 280 ) 281 if new_value_str.count(":") != 5: 282 raise esptool.FatalError( 283 "MAC Address needs to be a 6-byte hexadecimal format " 284 "separated by colons (:)!" 285 ) 286 hexad = new_value_str.replace(":", "") 287 if len(hexad) != 12: 288 raise esptool.FatalError( 289 "MAC Address needs to be a 6-byte hexadecimal number " 290 "(12 hexadecimal characters)!" 291 ) 292 # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', 293 bindata = binascii.unhexlify(hexad) 294 # unicast address check according to 295 # https://tools.ietf.org/html/rfc7042#section-2.1 296 if esptool.util.byte(bindata, 0) & 0x01: 297 raise esptool.FatalError("Custom MAC must be a unicast MAC!") 298 return bindata 299 300 @staticmethod 301 def get_and_check(raw_mac, stored_crc): 302 computed_crc = EfuseMacField.calc_crc(raw_mac) 303 if computed_crc == stored_crc: 304 valid_msg = "(CRC 0x%02x OK)" % stored_crc 305 else: 306 valid_msg = "(CRC 0x%02x invalid - calculated 0x%02x)" % ( 307 stored_crc, 308 computed_crc, 309 ) 310 return "%s %s" % (util.hexify(raw_mac, ":"), valid_msg) 311 312 @staticmethod 313 def calc_crc(raw_mac): 314 """ 315 This algorithm is the equivalent of esp_crc8() in ESP32 ROM code 316 317 This is CRC-8 w/ inverted polynomial value 0x8C & initial value 0x00. 318 """ 319 result = 0x00 320 for b in struct.unpack("B" * 6, raw_mac): 321 result ^= b 322 for _ in range(8): 323 lsb = result & 1 324 result >>= 1 325 if lsb != 0: 326 result ^= 0x8C 327 return result 328 329 def get(self, from_read=True): 330 if self.name == "CUSTOM_MAC": 331 mac = self.get_raw(from_read)[::-1] 332 stored_crc = self.parent["CUSTOM_MAC_CRC"].get(from_read) 333 else: 334 mac = self.get_raw(from_read) 335 stored_crc = self.parent["MAC_CRC"].get(from_read) 336 return EfuseMacField.get_and_check(mac, stored_crc) 337 338 def save(self, new_value): 339 def print_field(e, new_value): 340 print( 341 " - '{}' ({}) {} -> {}".format( 342 e.name, e.description, e.get_bitstring(), new_value 343 ) 344 ) 345 346 if self.name == "CUSTOM_MAC": 347 # Writing the BLK3: 348 # - MAC_VERSION = 1 349 # - CUSTOM_MAC = AB:CD:EF:01:02:03 350 # - CUSTOM_MAC_CRC = crc8(CUSTOM_MAC) 351 mac_version = self.parent["MAC_VERSION"] 352 if mac_version.get() == 0: 353 mac_version_value = 1 354 print_field(mac_version, hex(mac_version_value)) 355 mac_version.save(mac_version_value) 356 else: 357 if mac_version.get() != 1: 358 if not self.parent.force_write_always: 359 raise esptool.FatalError( 360 "MAC_VERSION = {}, should be 0 or 1.".format( 361 mac_version.get() 362 ) 363 ) 364 365 bitarray_mac = self.convert_to_bitstring(new_value) 366 print_field(self, bitarray_mac) 367 super(EfuseMacField, self).save(new_value) 368 369 crc_val = self.calc_crc(new_value) 370 crc_field = self.parent["CUSTOM_MAC_CRC"] 371 print_field(crc_field, hex(crc_val)) 372 crc_field.save(crc_val) 373 else: 374 # Writing the BLK0 default MAC is not possible, 375 # as it's written in the factory. 376 raise esptool.FatalError("Writing Factory MAC address is not supported") 377 378 379class EfuseWafer(EfuseField): 380 def get(self, from_read=True): 381 rev_bit0 = self.parent["CHIP_VER_REV1"].get(from_read) 382 assert self.parent["CHIP_VER_REV1"].bit_len == 1 383 rev_bit1 = self.parent["CHIP_VER_REV2"].get(from_read) 384 assert self.parent["CHIP_VER_REV2"].bit_len == 1 385 apb_ctl_date = self.parent.read_reg(self.parent.REGS.APB_CTL_DATE_ADDR) 386 rev_bit2 = ( 387 apb_ctl_date >> self.parent.REGS.APB_CTL_DATE_S 388 ) & self.parent.REGS.APB_CTL_DATE_V 389 combine_value = (rev_bit2 << 2) | (rev_bit1 << 1) | rev_bit0 390 391 revision = { 392 0: 0, 393 1: 1, 394 3: 2, 395 7: 3, 396 }.get(combine_value, 0) 397 return revision 398 399 def save(self, new_value): 400 raise esptool.FatalError("Burning %s is not supported" % self.name) 401 402 403class EfusePkg(EfuseField): 404 def get(self, from_read=True): 405 lo_bits = self.parent["CHIP_PACKAGE"].get(from_read) 406 hi_bits = self.parent["CHIP_PACKAGE_4BIT"].get(from_read) 407 return (hi_bits << 3) + lo_bits 408 409 def save(self, new_value): 410 raise esptool.FatalError("Burning %s is not supported" % self.name) 411 412 413class EfuseSpiPinField(EfuseField): 414 def get(self, from_read=True): 415 val = self.get_raw(from_read) 416 if val >= 30: 417 val += 2 # values 30,31 map to 32, 33 418 return val 419 420 def check_format(self, new_value_str): 421 if new_value_str is None: 422 return new_value_str 423 424 new_value_int = int(new_value_str, 0) 425 426 if new_value_int in [30, 31]: 427 raise esptool.FatalError( 428 "IO pins 30 & 31 cannot be set for SPI flash. 0-29, 32 & 33 only." 429 ) 430 elif new_value_int > 33: 431 raise esptool.FatalError( 432 "IO pin %d cannot be set for SPI flash. 0-29, 32 & 33 only." 433 % new_value_int 434 ) 435 elif new_value_int in [32, 33]: 436 return str(new_value_int - 2) 437 else: 438 return new_value_str 439 440 441class EfuseVRefField(EfuseField): 442 VREF_OFFSET = 1100 # ideal efuse value in mV 443 VREF_STEP_SIZE = 7 # 1 count in efuse == 7mV 444 VREF_SIGN_BIT = 0x10 445 VREF_MAG_BITS = 0x0F 446 447 def get(self, from_read=True): 448 val = self.get_raw(from_read) 449 # sign-magnitude format 450 if val & self.VREF_SIGN_BIT: 451 val = -(val & self.VREF_MAG_BITS) 452 else: 453 val = val & self.VREF_MAG_BITS 454 val *= self.VREF_STEP_SIZE 455 return self.VREF_OFFSET + val 456 457 def save(self, new_value): 458 raise esptool.FatalError("Writing to VRef is not supported.") 459 460 461class EfuseAdcPointCalibration(EfuseField): 462 TP_OFFSET = { # See TP_xxxx_OFFSET in esp_adc_cal.c in ESP-IDF 463 "ADC1_TP_LOW": 278, 464 "ADC2_TP_LOW": 421, 465 "ADC1_TP_HIGH": 3265, 466 "ADC2_TP_HIGH": 3406, 467 } 468 SIGN_BIT = (0x40, 0x100) # LOW, HIGH (2s complement format) 469 STEP_SIZE = 4 470 471 def get(self, from_read=True): 472 idx = 0 if self.name.endswith("LOW") else 1 473 sign_bit = self.SIGN_BIT[idx] 474 offset = self.TP_OFFSET[self.name] 475 raw = self.get_raw() 476 delta = (raw & (sign_bit - 1)) - (raw & sign_bit) 477 return offset + (delta * self.STEP_SIZE) 478