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