Qualify GAT562 bootloader updates

This commit is contained in:
mikecarper committed 2026-09-03 15:48:35 -07:00
1 parent dea2ce495f
commit f2c15225cc
4 files changed
+56 -2

No files matched your search

+8 -1
View File
@@ -103,7 +103,8 @@ manifest identity:
| `ThinkNode_M3_repeater_lora_ota_no_external_sensors` | `239A00DA / TNM3_DFU` | `0CA41DB2` |
| `RAK_3401_repeater_lora_ota_no_external_sensors` | `239A0029 / 3401_DFU` | `23818A80` |
| RAK4631 lean repeater and both lean RS232 bridges | `239A0029 / 4631_DFU` | `2D0DF000` |
| GAT562 30S/Tracker Pro/EVB Pro and R1Neo lean carrier aliases | `239A0029 / 4631_DFU` | `2D0DF000` |
| GAT562 30S/Tracker Pro/EVB Pro lean targets | `239A0029 / GAT562_DFU` | `D50D2D44` |
| `R1Neo_repeater_lora_ota_no_external_sensors` | `239A0029 / 4631_DFU` | `2D0DF000` |
| `RAK_WisMesh_Tag_repeater_lora_ota_no_external_sensors` | `239A0029 / RTAG_DFU` | `C72E9C9C` |
Board IDs are not globally unique. For generic targets, the signed hardware ID
@@ -118,6 +119,12 @@ duplicates and collisions with application target IDs. Generic image parsing
can inspect a future canonical identity, but signing/building a package fails
until that exact identity is in the qualified inventory.
Earlier GAT562 installations may report the RAK-compatible `4631_DFU`
identity. The installed and candidate identity pair must match exactly, so a
remote bootloader update cannot migrate one of those devices to `GAT562_DFU`.
Provision the GAT562-specific OTAFIX bootloader once through USB/BLE DFU or SWD;
subsequent signed bootloader mOTA updates use target `D50D2D44`.
## Embedded continuity and version policy
Every remotely supplied successor retains the legacy 44-byte CRC-valid `BLMF`
+7 -1
View File
@@ -378,14 +378,19 @@ TEST(OtaBootPackage, SdApprovalTokenBindsTheAuthenticatedImageHash) {
TEST(OtaBootPackage, GenericIdentityUsesBoardAndNameForCollisionSafeTarget) {
const uint32_t shared_board_id = 0x239A0029u;
const uint8_t gat562_name[16] = {'G','A','T','5','6','2','_','D','F','U',0,0,0,0,0,0};
const uint8_t rak3401_name[16] = {'3','4','0','1','_','D','F','U',0,0,0,0,0,0,0,0};
const uint8_t rak4631_name[16] = {'4','6','3','1','_','D','F','U',0,0,0,0,0,0,0,0};
uint8_t hw3401[32], hw4631[32];
uint8_t hw_gat562[32], hw3401[32], hw4631[32];
ASSERT_TRUE(ota_bootloader_hw_id(shared_board_id, gat562_name, hw_gat562));
ASSERT_TRUE(ota_bootloader_hw_id(shared_board_id, rak3401_name, hw3401));
ASSERT_TRUE(ota_bootloader_hw_id(shared_board_id, rak4631_name, hw4631));
EXPECT_EQ(0, memcmp(hw_gat562, "NRF_BL_239A0029_GAT562_DFU", 26));
EXPECT_EQ(0, memcmp(hw3401, "NRF_BL_239A0029_3401_DFU", 24));
EXPECT_EQ(0, memcmp(hw4631, "NRF_BL_239A0029_4631_DFU", 24));
EXPECT_NE(0, memcmp(hw_gat562, hw4631, sizeof(hw_gat562)));
EXPECT_NE(0, memcmp(hw3401, hw4631, sizeof(hw3401)));
EXPECT_EQ(ota_bootloader_target_id(shared_board_id, gat562_name), 0xD50D2D44u);
EXPECT_EQ(ota_bootloader_target_id(shared_board_id, rak3401_name), 0x23818A80u);
EXPECT_EQ(ota_bootloader_target_id(shared_board_id, rak4631_name), 0x2D0DF000u);
@@ -418,6 +423,7 @@ TEST(OtaBootPackage, GenericIdentityUsesBoardAndNameForCollisionSafeTarget) {
struct KnownIdentity { uint32_t board_id; const char* name; uint32_t target_id; };
const KnownIdentity known[] = {
{0x239A0029u, "GAT562_DFU", 0xD50D2D44u},
{0x239A0071u, "TOWER_V2_OTA", 0x1150F50Eu},
{0x239A0071u, "T096_DFU", 0x42354C85u},
{0x239A0071u, "T1_DFU", 0xFC556FFCu},
+1
View File
@@ -119,6 +119,7 @@ BOOT_REQUIRED_APP_CODEC_MASK = ((1 << CODEC_FULL) |
# aliases use the same installed bootloader identity and therefore do not add
# rows here.
INTERNAL_BOOTLOADER_IDENTITIES = (
(0x239A0029, "GAT562_DFU"),
(0x239A0071, "TOWER_V2_OTA"),
(0x239A0071, "T096_DFU"),
(0x239A0071, "T1_DFU"),
+40
View File
@@ -53,6 +53,13 @@ def test_internal_bootloader_target_wiring_is_central_and_not_duplicated():
]
assert len(inventory) == len(set(inventory))
assert len(inventory) == 21
gat_targets = {
"GAT562_30S_Mesh_Kit_repeater_lora_ota_no_external_sensors",
"GAT562_Mesh_Tracker_Pro_repeater_lora_ota_no_external_sensors",
"GAT562_Mesh_EVB_Pro_repeater_lora_ota_no_external_sensors",
}
assert gat_targets <= set(inventory)
assert not any("GAT562_Mesh_Watch13" in target for target in inventory)
target_header = (root / "src/helpers/ota/OtaTargets.h").read_text(encoding="utf-8")
for target in inventory:
@@ -1379,6 +1386,38 @@ def test_generic_internal_bootloader_build_parse_and_strict_contract():
pass
def test_gat562_internal_bootloader_profile_is_exact():
priv = Ed25519PrivateKey.generate()
image = _generic_bootloader_image(device_name="GAT562_DFU")
identity = ml.validate_bootloader_image(image)
assert (identity.board_id, identity.device_name) == (0x239A0029, "GAT562_DFU")
profile = ml.bootloader_qualified_platform_profile(
identity.board_id, identity.device_name)
assert profile == (
ml.BOOT_CONTINUITY_FAMILY_S140,
ml.BOOTLOADER_S140_V6_FWID,
ml.NRF52_APP_BASE_S140_V6,
ml.BOOT_CONTINUITY_LAYOUT_ABI,
)
storages = ml.bootloader_qualified_storage_profiles(
identity.board_id, identity.device_name)
assert storages == (ml.BOOT_STORAGE_INTERNAL_UPDATE,)
target = ml.bootloader_target_id(identity.board_id, identity.device_name)
expected_hw = b"NRF_BL_239A0029_GAT562_DFU".ljust(32, b"\0")
assert target == 0xD50D2D44
assert ml.bootloader_hw_id(identity.board_id, identity.device_name) == expected_hw
manifest = ml.build_manifest(
target_id=target, fw_version=identity.boot_version,
image_size=len(image), payload=image, block_size=1024,
image_hash=ml.mh32(image), codec_id=ml.CODEC_FULL, is_full=True,
sign_priv=priv, bootloader=True)
parsed = ml.parse_container(ml.build_container(manifest, image))
assert parsed.manifest.target_id == target
assert parsed.manifest.hw_id == expected_hw
assert ml.verify(parsed, expect_pub=ml.ed25519_public_bytes(priv)) == []
def test_meshtower_sd_bootloader_profile_builds_the_same_exact_identity():
priv = Ed25519PrivateKey.generate()
image = _generic_bootloader_image(
@@ -1418,6 +1457,7 @@ def test_bootloader_build_inventory_is_unique_and_disjoint_from_app_targets():
import re
expected = {
(0x239A0029, "GAT562_DFU"): 0xD50D2D44,
(0x239A0071, "TOWER_V2_OTA"): 0x1150F50E,
(0x239A0071, "T096_DFU"): 0x42354C85,
(0x239A0071, "T1_DFU"): 0xFC556FFC,