Skip to content

Commit

Permalink
esptool & espefuse: Fix byte order in MAC (for C6 and H2)
Browse files Browse the repository at this point in the history
MAC: 60:55:f9:ff:fe:f7:2c:a2 (EUI64, used for IEEE802154)
BASE MAC: 60:55:f9:f7:2c:a2 (used for BT)
MAC_EXT: ff:fe
  • Loading branch information
KonstantinKondrashov authored and radimkarnis committed Jun 12, 2023
1 parent f4b5914 commit 6a17e7f
Show file tree
Hide file tree
Showing 20 changed files with 153 additions and 85 deletions.
4 changes: 2 additions & 2 deletions docs/en/espefuse/inc/summary_ESP32-H2.rst
Expand Up @@ -62,10 +62,10 @@
Mac fuses:
MAC (BLOCK1) MAC address
= 60:55:f9:f7:2c:05:ff:fe (OK) R/W
= 60:55:f9:f7:2c:05 (OK) R/W
MAC_EXT (BLOCK1) Stores the extended bits of MAC address = ff:fe (OK) R/W
CUSTOM_MAC (BLOCK3) Custom MAC
= 00:00:00:00:00:00:ff:fe (OK) R/W
= 00:00:00:00:00:00 (OK) R/W
Security fuses:
DIS_FORCE_DOWNLOAD (BLOCK0) Represents whether the function that forces chip i = False R/W (0b0)
Expand Down
9 changes: 7 additions & 2 deletions espefuse/efuse/base_fields.py
Expand Up @@ -577,6 +577,9 @@ def __init__(self, parent, param):
self.bitarray.set(0)
self.update(self.parent.blocks[self.block].bitarray)

def is_field_calculated(self):
return self.word is None or self.pos is None

def check_format(self, new_value_str):
if new_value_str is None:
return new_value_str
Expand Down Expand Up @@ -669,8 +672,10 @@ def save(self, new_value):
self.save_to_block(bitarray_field)

def update(self, bit_array_block):
if self.word is None or self.pos is None:
self.bitarray.overwrite(self.convert_to_bitstring(self.get()), pos=0)
if self.is_field_calculated():
self.bitarray.overwrite(
self.convert_to_bitstring(self.check_format(self.get())), pos=0
)
return
field_len = self.bitarray.len
bit_array_block.pos = bit_array_block.length - (
Expand Down
33 changes: 22 additions & 11 deletions espefuse/efuse/esp32c6/fields.py
Expand Up @@ -326,23 +326,27 @@ def check_format(self, new_value_str):
raise esptool.FatalError(
"Required MAC Address in AA:CD:EF:01:02:03 format!"
)
if new_value_str.count(":") != 5:
num_bytes = 8 if self.name == "MAC_EUI64" else 6
if new_value_str.count(":") != num_bytes - 1:
raise esptool.FatalError(
"MAC Address needs to be a 6-byte hexadecimal format "
f"MAC Address needs to be a {num_bytes}-byte hexadecimal format "
"separated by colons (:)!"
)
hexad = new_value_str.replace(":", "")
if len(hexad) != 12:
hexad = new_value_str.replace(":", "").split(" ", 1)[0]
hexad = hexad.split(" ", 1)[0] if self.is_field_calculated() else hexad
if len(hexad) != num_bytes * 2:
raise esptool.FatalError(
"MAC Address needs to be a 6-byte hexadecimal number "
"(12 hexadecimal characters)!"
f"MAC Address needs to be a {num_bytes}-byte hexadecimal number "
f"({num_bytes * 2} hexadecimal characters)!"
)
# order of bytearray = b'\xaa\xcd\xef\x01\x02\x03',
bindata = binascii.unhexlify(hexad)
# unicast address check according to
# https://tools.ietf.org/html/rfc7042#section-2.1
if esptool.util.byte(bindata, 0) & 0x01:
raise esptool.FatalError("Custom MAC must be a unicast MAC!")

if not self.is_field_calculated():
# unicast address check according to
# https://tools.ietf.org/html/rfc7042#section-2.1
if esptool.util.byte(bindata, 0) & 0x01:
raise esptool.FatalError("Custom MAC must be a unicast MAC!")
return bindata

def check(self):
Expand All @@ -356,6 +360,13 @@ def check(self):
def get(self, from_read=True):
if self.name == "CUSTOM_MAC":
mac = self.get_raw(from_read)[::-1]
elif self.name == "MAC":
mac = self.get_raw(from_read)
elif self.name == "MAC_EUI64":
mac = self.parent["MAC"].get_bitstring(from_read).copy()
mac_ext = self.parent["MAC_EXT"].get_bitstring(from_read)
mac.insert(mac_ext, 24)
mac = mac.bytes
else:
mac = self.get_raw(from_read)
return "%s %s" % (util.hexify(mac, ":"), self.check())
Expand All @@ -375,7 +386,7 @@ def print_field(e, new_value):
else:
# Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible,
# as it's written in the factory.
raise esptool.FatalError("Writing Factory MAC address is not supported")
raise esptool.FatalError(f"Burning {self.name} is not supported")


# fmt: off
Expand Down
11 changes: 11 additions & 0 deletions espefuse/efuse/esp32c6/mem_definition.py
Expand Up @@ -12,6 +12,7 @@
EfuseBlocksBase,
EfuseFieldsBase,
EfuseRegistersBase,
Field,
)


Expand Down Expand Up @@ -151,6 +152,16 @@ def __init__(self) -> None:
self.BLOCK2_CALIBRATION_EFUSES.append(efuse)
self.ALL_EFUSES[i] = None

f = Field()
f.name = "MAC_EUI64"
f.block = 1
f.bit_len = 64
f.type = f"bytes:{f.bit_len // 8}"
f.category = "MAC"
f.class_type = "mac"
f.description = "calc MAC_EUI64 = MAC[0]:MAC[1]:MAC[2]:MAC_EXT[0]:MAC_EXT[1]:MAC[3]:MAC[4]:MAC[5]"
self.CALC.append(f)

for efuse in self.ALL_EFUSES:
if efuse is not None:
self.EFUSES.append(efuse)
Expand Down
3 changes: 1 addition & 2 deletions espefuse/efuse/esp32c6/operations.py
Expand Up @@ -167,8 +167,7 @@ def add_commands(subparsers, efuses):
p.add_argument(
"mac",
help="Custom MAC Address to burn given in hexadecimal format with bytes "
"separated by colons (e.g. AA:CD:EF:01:02:03). "
"Final CUSTOM_MAC = CUSTOM_MAC[48] + MAC_EXT[16]",
"separated by colons (e.g. AA:CD:EF:01:02:03).",
type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"),
)
add_force_write_always(p)
Expand Down
35 changes: 21 additions & 14 deletions espefuse/efuse/esp32h2/fields.py
Expand Up @@ -327,23 +327,27 @@ def check_format(self, new_value_str):
raise esptool.FatalError(
"Required MAC Address in AA:CD:EF:01:02:03 format!"
)
if new_value_str.count(":") != 5:
num_bytes = 8 if self.name == "MAC_EUI64" else 6
if new_value_str.count(":") != num_bytes - 1:
raise esptool.FatalError(
"MAC Address needs to be a 6-byte hexadecimal format "
f"MAC Address needs to be a {num_bytes}-byte hexadecimal format "
"separated by colons (:)!"
)
hexad = new_value_str.replace(":", "")
if len(hexad) != 12:
hexad = hexad.split(" ", 1)[0] if self.is_field_calculated() else hexad
if len(hexad) != num_bytes * 2:
raise esptool.FatalError(
"MAC Address needs to be a 6-byte hexadecimal number "
"(12 hexadecimal characters)!"
f"MAC Address needs to be a {num_bytes}-byte hexadecimal number "
f"({num_bytes * 2} hexadecimal characters)!"
)
# order of bytearray = b'\xaa\xcd\xef\x01\x02\x03',
bindata = binascii.unhexlify(hexad)
# unicast address check according to
# https://tools.ietf.org/html/rfc7042#section-2.1
if esptool.util.byte(bindata, 0) & 0x01:
raise esptool.FatalError("Custom MAC must be a unicast MAC!")

if not self.is_field_calculated():
# unicast address check according to
# https://tools.ietf.org/html/rfc7042#section-2.1
if esptool.util.byte(bindata, 0) & 0x01:
raise esptool.FatalError("Custom MAC must be a unicast MAC!")
return bindata

def check(self):
Expand All @@ -356,11 +360,14 @@ def check(self):

def get(self, from_read=True):
if self.name == "CUSTOM_MAC":
mac = self.get_raw(from_read)[::-1] + self.parent["MAC_EXT"].get_raw(
from_read
)
mac = self.get_raw(from_read)[::-1]
elif self.name == "MAC":
mac = self.get_raw(from_read) + self.parent["MAC_EXT"].get_raw(from_read)
mac = self.get_raw(from_read)
elif self.name == "MAC_EUI64":
mac = self.parent["MAC"].get_bitstring(from_read).copy()
mac_ext = self.parent["MAC_EXT"].get_bitstring(from_read)
mac.insert(mac_ext, 24)
mac = mac.bytes
else:
mac = self.get_raw(from_read)
return "%s %s" % (util.hexify(mac, ":"), self.check())
Expand All @@ -380,7 +387,7 @@ def print_field(e, new_value):
else:
# Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible,
# as it's written in the factory.
raise esptool.FatalError("Writing Factory MAC address is not supported")
raise esptool.FatalError(f"Burning {self.name} is not supported")


# fmt: off
Expand Down
17 changes: 16 additions & 1 deletion espefuse/efuse/esp32h2/mem_definition.py
Expand Up @@ -8,7 +8,12 @@

import yaml

from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase
from ..mem_definition_base import (
EfuseBlocksBase,
EfuseFieldsBase,
EfuseRegistersBase,
Field,
)


class EfuseDefineRegisters(EfuseRegistersBase):
Expand Down Expand Up @@ -147,6 +152,16 @@ def __init__(self) -> None:
self.BLOCK2_CALIBRATION_EFUSES.append(efuse)
self.ALL_EFUSES[i] = None

f = Field()
f.name = "MAC_EUI64"
f.block = 1
f.bit_len = 64
f.type = f"bytes:{f.bit_len // 8}"
f.category = "MAC"
f.class_type = "mac"
f.description = "calc MAC_EUI64 = MAC[0]:MAC[1]:MAC[2]:MAC_EXT[0]:MAC_EXT[1]:MAC[3]:MAC[4]:MAC[5]"
self.CALC.append(f)

for efuse in self.ALL_EFUSES:
if efuse is not None:
self.EFUSES.append(efuse)
Expand Down
3 changes: 1 addition & 2 deletions espefuse/efuse/esp32h2/operations.py
Expand Up @@ -166,8 +166,7 @@ def add_commands(subparsers, efuses):
p.add_argument(
"mac",
help="Custom MAC Address to burn given in hexadecimal format with bytes "
"separated by colons (e.g. AA:CD:EF:01:02:03). "
"Final CUSTOM_MAC = CUSTOM_MAC[48] + MAC_EXT[16]",
"separated by colons (e.g. AA:CD:EF:01:02:03).",
type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"),
)
add_force_write_always(p)
Expand Down
36 changes: 22 additions & 14 deletions espefuse/efuse/esp32h2beta1/fields.py
Expand Up @@ -326,23 +326,27 @@ def check_format(self, new_value_str):
raise esptool.FatalError(
"Required MAC Address in AA:CD:EF:01:02:03 format!"
)
if new_value_str.count(":") != 5:
num_bytes = 8 if self.name == "MAC_EUI64" else 6
if new_value_str.count(":") != num_bytes - 1:
raise esptool.FatalError(
"MAC Address needs to be a 6-byte hexadecimal format "
f"MAC Address needs to be a {num_bytes}-byte hexadecimal format "
"separated by colons (:)!"
)
hexad = new_value_str.replace(":", "")
if len(hexad) != 12:
hexad = new_value_str.replace(":", "").split(" ", 1)[0]
hexad = hexad.split(" ", 1)[0] if self.is_field_calculated() else hexad
if len(hexad) != num_bytes * 2:
raise esptool.FatalError(
"MAC Address needs to be a 6-byte hexadecimal number "
"(12 hexadecimal characters)!"
f"MAC Address needs to be a {num_bytes}-byte hexadecimal number "
f"({num_bytes * 2} hexadecimal characters)!"
)
# order of bytearray = b'\xaa\xcd\xef\x01\x02\x03',
bindata = binascii.unhexlify(hexad)
# unicast address check according to
# https://tools.ietf.org/html/rfc7042#section-2.1
if esptool.util.byte(bindata, 0) & 0x01:
raise esptool.FatalError("Custom MAC must be a unicast MAC!")

if not self.is_field_calculated():
# unicast address check according to
# https://tools.ietf.org/html/rfc7042#section-2.1
if esptool.util.byte(bindata, 0) & 0x01:
raise esptool.FatalError("Custom MAC must be a unicast MAC!")
return bindata

def check(self):
Expand All @@ -355,11 +359,14 @@ def check(self):

def get(self, from_read=True):
if self.name == "CUSTOM_MAC":
mac = self.get_raw(from_read)[::-1] + self.parent["MAC_EXT"].get_raw(
from_read
)
mac = self.get_raw(from_read)[::-1]
elif self.name == "MAC":
mac = self.get_raw(from_read) + self.parent["MAC_EXT"].get_raw(from_read)
mac = self.get_raw(from_read)
elif self.name == "MAC_EUI64":
mac = self.parent["MAC"].get_bitstring(from_read).copy()
mac_ext = self.parent["MAC_EXT"].get_bitstring(from_read)
mac.insert(mac_ext, 24)
mac = mac.bytes
else:
mac = self.get_raw(from_read)
return "%s %s" % (util.hexify(mac, ":"), self.check())
Expand All @@ -379,6 +386,7 @@ def print_field(e, new_value):
else:
# Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible,
# as it's written in the factory.
raise esptool.FatalError(f"Burning {self.name} is not supported")
raise esptool.FatalError("Writing Factory MAC address is not supported")


Expand Down
3 changes: 1 addition & 2 deletions espefuse/efuse/esp32h2beta1/operations.py
Expand Up @@ -166,8 +166,7 @@ def add_commands(subparsers, efuses):
p.add_argument(
"mac",
help="Custom MAC Address to burn given in hexadecimal format with bytes "
"separated by colons (e.g. AA:CD:EF:01:02:03). "
"Final CUSTOM_MAC = CUSTOM_MAC[48] + MAC_EXT[16]",
"separated by colons (e.g. AA:CD:EF:01:02:03).",
type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"),
)
add_force_write_always(p)
Expand Down
10 changes: 7 additions & 3 deletions esptool/cmds.py
Expand Up @@ -1029,12 +1029,16 @@ def elf2image(args):


def read_mac(esp, args):
mac = esp.read_mac()

def print_mac(label, mac):
print("%s: %s" % (label, ":".join(map(lambda x: "%02x" % x, mac))))

print_mac("MAC", mac)
eui64 = esp.read_mac("EUI64")
if eui64:
print_mac("MAC", eui64)
print_mac("BASE MAC", esp.read_mac("BASE_MAC"))
print_mac("MAC_EXT", esp.read_mac("MAC_EXT"))
else:
print_mac("MAC", esp.read_mac("BASE_MAC"))


def chip_id(esp, args):
Expand Down
4 changes: 3 additions & 1 deletion esptool/targets/esp32.py
Expand Up @@ -283,8 +283,10 @@ def read_efuse(self, n):
def chip_id(self):
raise NotSupportedError(self, "chip_id")

def read_mac(self):
def read_mac(self, mac_type="BASE_MAC"):
"""Read MAC from EFUSE region"""
if mac_type != "BASE_MAC":
return None
words = [self.read_efuse(2), self.read_efuse(1)]
bitstring = struct.pack(">II", *words)
bitstring = bitstring[2:8] # trim the 2 byte CRC
Expand Down
5 changes: 4 additions & 1 deletion esptool/targets/esp32c3.py
Expand Up @@ -134,7 +134,10 @@ def override_vddsdio(self, new_voltage):
"VDD_SDIO overrides are not supported for ESP32-C3"
)

def read_mac(self):
def read_mac(self, mac_type="BASE_MAC"):
"""Read MAC from EFUSE region"""
if mac_type != "BASE_MAC":
return None
mac0 = self.read_reg(self.MAC_EFUSE_REG)
mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC
bitstring = struct.pack(">II", mac1, mac0)[2:]
Expand Down
23 changes: 15 additions & 8 deletions esptool/targets/esp32c6.py
Expand Up @@ -134,15 +134,22 @@ def override_vddsdio(self, new_voltage):
"VDD_SDIO overrides are not supported for ESP32-C6"
)

def read_mac(self):
def read_mac(self, mac_type="BASE_MAC"):
"""Read MAC from EFUSE region"""
mac0 = self.read_reg(self.MAC_EFUSE_REG)
mac_reg1 = self.read_reg(self.MAC_EFUSE_REG + 4)
mac1 = mac_reg1 & 0xFFFF
mac_ext = (mac_reg1 >> 16) & 0xFFFF
bitstring = struct.pack(">HIH", mac1, mac0, mac_ext)
# MAC: 60:55:f9:f7:2c:a2:ff:fe
# | mac1| mac0 | mac_ext|
return tuple(bitstring)
mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC
base_mac = struct.pack(">II", mac1, mac0)[2:]
ext_mac = struct.pack(">H", (mac1 >> 16) & 0xFFFF)
eui64 = base_mac[0:3] + ext_mac + base_mac[3:6]
# BASE MAC: 60:55:f9:f7:2c:a2
# EUI64 MAC: 60:55:f9:ff:fe:f7:2c:a2
# EXT_MAC: ff:fe
macs = {
"BASE_MAC": tuple(base_mac),
"EUI64": tuple(eui64),
"MAC_EXT": tuple(ext_mac),
}
return macs.get(mac_type, None)

def get_flash_crypt_config(self):
return None # doesn't exist on ESP32-C6
Expand Down
2 changes: 1 addition & 1 deletion esptool/targets/esp32h2.py
Expand Up @@ -50,7 +50,7 @@ def get_chip_description(self):
return f"{chip_name} (revision v{major_rev}.{minor_rev})"

def get_chip_features(self):
return ["BLE"]
return ["BLE", "IEEE802.15.4"]

def get_crystal_freq(self):
# ESP32H2 XTAL is fixed to 32MHz
Expand Down

0 comments on commit 6a17e7f

Please sign in to comment.