Skip to content

Commit a1436bf

Browse files
committed
Log an error instead of crashing when loading state from a bad CC2531
Some users have been using CC2531 adapters that were flashed with Z-Stack 3 *without being erased*. Their TCLK NVRAM entry is too big but Z-Stack seems to still run.
1 parent 03ee732 commit a1436bf

File tree

4 files changed

+54
-14
lines changed

4 files changed

+54
-14
lines changed

tests/api/test_network_state.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1+
import logging
12
import dataclasses
23

34
import pytest
45

5-
from ..conftest import ALL_DEVICES, FORMED_DEVICES, BaseZStack1CC2531
6+
from zigpy_znp.types.nvids import ExNvIds, OsalNvIds
7+
8+
from ..conftest import (
9+
ALL_DEVICES,
10+
FORMED_DEVICES,
11+
BaseZStack1CC2531,
12+
FormedZStack3CC2531,
13+
)
614

715

816
@pytest.mark.parametrize("to_device", ALL_DEVICES)
@@ -35,3 +43,19 @@ async def test_state_transfer(from_device, to_device, make_connected_znp):
3543
assert formed_znp.network_info == empty_znp.network_info
3644

3745
assert formed_znp.node_info == empty_znp.node_info
46+
47+
48+
@pytest.mark.parametrize("device", [FormedZStack3CC2531])
49+
async def test_broken_cc2531_load_state(device, make_connected_znp, caplog):
50+
znp, znp_server = await make_connected_znp(server_cls=device)
51+
52+
# "Bad" TCLK seed is a TCLK from Z-Stack 1 with the first 16 bytes overwritten
53+
znp_server._nvram[ExNvIds.LEGACY][
54+
OsalNvIds.TCLK_SEED
55+
] += b"liance092\x00\x00\x00\x00\x00\x00\x00"
56+
57+
caplog.set_level(logging.ERROR)
58+
await znp.load_network_info()
59+
assert "inconsistent" in caplog.text
60+
61+
znp.close()

zigpy_znp/api.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,31 @@ async def load_network_info(self, *, load_devices=False):
142142
stack_specific=None,
143143
)
144144

145+
tclk_seed = None
146+
145147
if self.version > 1.2:
146-
tclk_seed = await self.nvram.osal_read(
147-
OsalNvIds.TCLK_SEED, item_type=t.KeyData
148-
)
148+
try:
149+
tclk_seed = await self.nvram.osal_read(
150+
OsalNvIds.TCLK_SEED, item_type=t.KeyData
151+
)
152+
except ValueError:
153+
if self.version != 3.0:
154+
raise
155+
156+
# CC2531s that have been cross-flashed from 1.2 -> 3.0 can have NVRAM
157+
# entries from both. Ignore deserialization length errors for the
158+
# trailing data to allow them to be backed up.
159+
tclk_seed_value = await self.nvram.osal_read(
160+
OsalNvIds.TCLK_SEED, item_type=t.Bytes
161+
)
162+
163+
tclk_seed = self.nvram.deserialize(
164+
tclk_seed_value, t.KeyData, allow_trailing=True
165+
)
166+
LOGGER.error(
167+
"Your adapter's NVRAM is inconsistent! Perform a network backup,"
168+
" re-flash its firmware, and restore the backup."
169+
)
149170

150171
network_info.stack_specific = {
151172
"zstack": {
@@ -155,7 +176,7 @@ async def load_network_info(self, *, load_devices=False):
155176

156177
# This takes a few seconds
157178
if load_devices:
158-
for dev in await security.read_devices(self):
179+
for dev in await security.read_devices(self, tclk_seed=tclk_seed):
159180
if dev.node_info.nwk == 0xFFFE:
160181
dev = dev.replace(
161182
node_info=dataclasses.replace(dev.node_info, nwk=None)

zigpy_znp/tools/network_backup.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,10 @@ def zigpy_state_to_json_backup(
7777

7878
async def backup_network(znp: ZNP) -> t.JSONType:
7979
try:
80-
await znp.load_network_info()
80+
await znp.load_network_info(load_devices=True)
8181
except ValueError as e:
8282
raise RuntimeError("Failed to load network info") from e
8383

84-
await znp.load_network_info(load_devices=True)
85-
8684
obj = zigpy_state_to_json_backup(
8785
network_info=znp.network_info,
8886
node_info=znp.node_info,

zigpy_znp/znp/security.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -253,12 +253,9 @@ async def read_unhashed_link_keys(
253253
)
254254

255255

256-
async def read_devices(znp: ZNP) -> typing.Sequence[StoredDevice]:
257-
tclk_seed = None
258-
259-
if znp.version > 1.2:
260-
tclk_seed = await znp.nvram.osal_read(OsalNvIds.TCLK_SEED, item_type=t.KeyData)
261-
256+
async def read_devices(
257+
znp: ZNP, *, tclk_seed: t.KeyData | None
258+
) -> typing.Sequence[StoredDevice]:
262259
addr_mgr = await read_addr_manager_entries(znp)
263260
devices = {}
264261

0 commit comments

Comments
 (0)