mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-10-23 20:22:26 +00:00
Add MeshTower SD bootloader updates over LoRa
This commit is contained in:
1 parent
9e4d246ac6
commit
3d78d9c846
26 files changed
+763
-114
No files matched your search
+1
-1
@@ -70,7 +70,7 @@ does not reflect the GoogleTest count -- run the built binary directly
|
||||
| `test_user_gpio` | `src/helpers/UserGpio.cpp`, `UserGpioReplyTracker.h` | board-approved pins, get/set/reset, timed nonblocking transitions, duplicate suppression, rollover, and completion-reply routing |
|
||||
| `test_utf8_helpers` | `src/helpers/UTF8Helpers.h` | byte-limit truncation at complete code-point boundaries and rejection of malformed or truncated UTF-8 |
|
||||
| `test_wifi_ota_seeder_policy` | `src/helpers/WiFiOtaSeederPolicy.h`, `WiFiOtaSeederStatus.h` | listener state versus network availability, serial/TCP folder ownership, detach detection, and bounded status formatting |
|
||||
| `test_ota` | `src/helpers/ota/` | v2 application/v3 bootloader parser separation; legacy XIAO and generic embedded identity, vector, capability, explicit-confirmation, codec-isolation, shared-slot placement/no-EndF erase gates, and no-autofetch gates; container and EndF integrity; protocol codecs; transfer, resume, and layered apply safety; adaptive 2-to-4 block-request window growth and stall contraction; active-transfer priority classification |
|
||||
| `test_ota` | `src/helpers/ota/` | v2 application/v3 bootloader parser separation; legacy XIAO, generic internal, and exact MeshTower V2 SD embedded identity, vector, capability, explicit-confirmation, codec-isolation, scratch-headroom/shared-slot no-EndF gates, and no-autofetch gates; container and EndF integrity; protocol codecs; transfer, resume, and layered apply safety; adaptive 2-to-4 block-request window growth and stall contraction; active-transfer priority classification |
|
||||
| `test_trace_retry` | `src/Mesh.cpp` retry and relay policy | app-v2 and boot-v3 traffic sharing `PAYLOAD_TYPE_OTA=0x0C` and the TempRadio suspend policy; opaque OTA relay behavior; background discovery priority; immediate primary transfer relay, receive-delay bypass, fast CAD retry, and no generic flood retry; trace and non-OTA flood retry timing |
|
||||
| `test_utils` | `src/Utils.cpp` | `Utils::toHex` (upstream) |
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include "helpers/ota/OtaManager.h"
|
||||
#include "helpers/ota/OtaApply.h"
|
||||
#include "helpers/ota/OtaBlInfo.h"
|
||||
#include "helpers/ota/OtaFlashLayout_nrf52.h"
|
||||
#include "helpers/ota/OtaSdBootToken.h"
|
||||
#include "mota_vectors.h" // auto-generated by tools/mota/gen_vectors.py
|
||||
|
||||
extern "C" {
|
||||
@@ -41,12 +43,74 @@ TEST(OtaBootResult, AcceptsOnlyOtafixApplyDiagnostics) {
|
||||
EXPECT_EQ(ota_nrf52_boot_result_or_zero(0x00), 0x00);
|
||||
EXPECT_EQ(ota_nrf52_boot_result_or_zero(0x4C), 0x00); // shutdown reason
|
||||
EXPECT_EQ(ota_nrf52_boot_result_or_zero(0x51), 0x00); // QSPI handoff
|
||||
EXPECT_EQ(ota_nrf52_boot_result_or_zero(0x53), 0x00); // SD handoff
|
||||
EXPECT_EQ(ota_nrf52_boot_result_or_zero(0xD4), 0x00); // legacy handoff
|
||||
EXPECT_EQ(ota_nrf52_boot_result_or_zero(0xED), 0x00); // expanded handoff
|
||||
EXPECT_EQ(ota_nrf52_boot_result_or_zero(0xE0), 0x00); // retired handoff remains non-result
|
||||
EXPECT_EQ(ota_nrf52_boot_result_or_zero(0xBD), 0x00);
|
||||
}
|
||||
|
||||
TEST(OtaBootPackage, StorageProfilesAndSdGeometryAreExact) {
|
||||
EXPECT_EQ(OTA_BL_PROFILE_SD_BOOT_UPDATE, 0x09u);
|
||||
EXPECT_EQ(OTA_BL_PROFILE_INTERNAL_BOOT_UPDATE, 0x0Au);
|
||||
EXPECT_EQ(OTA_BL_PROFILE_QSPI_BOOT_UPDATE, 0x0Eu);
|
||||
EXPECT_EQ(OTA_BL_REQUIRED_APP_CODEC_MASK, 0x0005u);
|
||||
EXPECT_EQ(GPREGRET2_OTA_STAGE_SD, 0x53u);
|
||||
EXPECT_TRUE(ota_bootloader_image_geometry_valid(
|
||||
OTA_BOOT_IMAGE_SIZE, OTA_BOOT_IMAGE_SIZE));
|
||||
EXPECT_FALSE(ota_bootloader_image_geometry_valid(
|
||||
OTA_BOOT_IMAGE_SIZE - 1u, OTA_BOOT_IMAGE_SIZE));
|
||||
EXPECT_FALSE(ota_bootloader_image_geometry_valid(
|
||||
OTA_BOOT_IMAGE_SIZE, OTA_BOOT_IMAGE_SIZE - 1u));
|
||||
EXPECT_TRUE(ota_bootloader_scratch_headroom_valid(
|
||||
true, MOTA_NRF52_APP_BASE_S140_V6,
|
||||
OTA_BOOT_SCRATCH_START - MOTA_NRF52_APP_BASE_S140_V6,
|
||||
OTA_BOOT_SCRATCH_START));
|
||||
EXPECT_FALSE(ota_bootloader_scratch_headroom_valid(
|
||||
false, MOTA_NRF52_APP_BASE_S140_V6, 1u, OTA_BOOT_SCRATCH_START));
|
||||
EXPECT_FALSE(ota_bootloader_scratch_headroom_valid(
|
||||
true, MOTA_NRF52_APP_BASE_S140_V6,
|
||||
OTA_BOOT_SCRATCH_START - MOTA_NRF52_APP_BASE_S140_V6 + 1u,
|
||||
OTA_BOOT_SCRATCH_START));
|
||||
|
||||
const uint32_t safe_span = OTA_BOOT_SCRATCH_START - MOTA_NRF52_APP_BASE_S140_V6;
|
||||
const uint32_t live_size = safe_span - 0x1000u;
|
||||
EXPECT_TRUE(ota_bootloader_scratch_bank_geometry_valid(
|
||||
OTA_BOOT_BANK_ERASED_FALLBACK, 0xFFFFu, UINT32_MAX, live_size, safe_span));
|
||||
EXPECT_TRUE(ota_bootloader_scratch_bank_geometry_valid(
|
||||
OTA_BOOT_BANK_VALID_APP, 0u, UINT32_MAX, live_size, safe_span));
|
||||
EXPECT_TRUE(ota_bootloader_scratch_bank_geometry_valid(
|
||||
OTA_BOOT_BANK_VALID_APP, 1u, live_size, live_size, safe_span));
|
||||
EXPECT_FALSE(ota_bootloader_scratch_bank_geometry_valid(
|
||||
OTA_BOOT_BANK_VALID_APP, 1u, live_size - 1u, live_size, safe_span));
|
||||
EXPECT_FALSE(ota_bootloader_scratch_bank_geometry_valid(
|
||||
OTA_BOOT_BANK_VALID_APP, 1u, safe_span + 1u, live_size, safe_span));
|
||||
EXPECT_FALSE(ota_bootloader_scratch_bank_geometry_valid(
|
||||
0x00FFu, 0u, 0u, live_size, safe_span));
|
||||
}
|
||||
|
||||
TEST(OtaBootPackage, SdApprovalTokenBindsTheAuthenticatedImageHash) {
|
||||
uint8_t hash[32];
|
||||
for (uint8_t i = 0; i < sizeof(hash); i++) hash[i] = i;
|
||||
uint8_t token[MOTA_SD_BOOT_TOKEN_LEN];
|
||||
mota_sd_boot_token_encode(token, MOTA_NRF52_BOOT_CONTAINER_SIZE, hash);
|
||||
EXPECT_EQ(0, memcmp(token, "MOTASDBL", 8));
|
||||
EXPECT_EQ(mota_sd_boot_token_rd32(token + 8), 1u);
|
||||
EXPECT_EQ(mota_sd_boot_token_rd32(token + 12), 64u);
|
||||
EXPECT_EQ(mota_sd_boot_token_rd32(token + 16), MOTA_NRF52_BOOT_CONTAINER_SIZE);
|
||||
EXPECT_EQ(mota_sd_boot_token_rd32(token + 20), ~MOTA_NRF52_BOOT_CONTAINER_SIZE);
|
||||
EXPECT_EQ(0, memcmp(token + MOTA_SD_BOOT_TOKEN_IMAGE_HASH_OFFSET, hash, sizeof(hash)));
|
||||
EXPECT_TRUE(mota_sd_boot_token_valid(
|
||||
token, MOTA_NRF52_BOOT_CONTAINER_SIZE, hash));
|
||||
|
||||
token[MOTA_SD_BOOT_TOKEN_IMAGE_HASH_OFFSET] ^= 1u;
|
||||
EXPECT_FALSE(mota_sd_boot_token_valid(
|
||||
token, MOTA_NRF52_BOOT_CONTAINER_SIZE, hash));
|
||||
token[MOTA_SD_BOOT_TOKEN_IMAGE_HASH_OFFSET] ^= 1u;
|
||||
EXPECT_FALSE(mota_sd_boot_token_valid(
|
||||
token, MOTA_NRF52_BOOT_CONTAINER_SIZE + 1u, hash));
|
||||
}
|
||||
|
||||
TEST(OtaBootPackage, GenericIdentityUsesBoardAndNameForCollisionSafeTarget) {
|
||||
const uint32_t shared_board_id = 0x239A0029u;
|
||||
const uint8_t rak3401_name[16] = {'3','4','0','1','_','D','F','U',0,0,0,0,0,0,0,0};
|
||||
@@ -209,7 +273,7 @@ TEST(OtaBootPackage, EmbeddedIdentityVectorCapsAndExplicitConfirmationGate) {
|
||||
image[0x300] ^= 1;
|
||||
EXPECT_FALSE(ota_bootloader_identity_from_image(image.data(), image.size(), identity));
|
||||
|
||||
uint8_t marker[16] = {'M','O','T','A','B','L','D','R', 3,0, 1,0,
|
||||
uint8_t marker[16] = {'M','O','T','A','B','L','D','R', 3,0, 5,0,
|
||||
(uint8_t)(OTA_BL_STORAGE_QSPI | OTA_BL_STORAGE_BOOT_UPDATE), 0,0,0};
|
||||
OtaBootloaderCapsMarker caps;
|
||||
EXPECT_TRUE(ota_bootloader_caps_marker_parse(marker, caps));
|
||||
@@ -218,7 +282,7 @@ TEST(OtaBootPackage, EmbeddedIdentityVectorCapsAndExplicitConfirmationGate) {
|
||||
|
||||
std::vector<uint8_t> internal_caps_image(64, 0xFF);
|
||||
const uint8_t internal_marker[16] = {
|
||||
'M','O','T','A','B','L','D','R', 3,0, 1,0,
|
||||
'M','O','T','A','B','L','D','R', 3,0, 5,0,
|
||||
(uint8_t)(OTA_BL_STORAGE_STAGE_CEILING | OTA_BL_STORAGE_BOOT_UPDATE), 0,0,0};
|
||||
memcpy(internal_caps_image.data() + 4, internal_marker, sizeof(internal_marker));
|
||||
EXPECT_TRUE(ota_bootloader_caps_from_image(
|
||||
@@ -237,6 +301,14 @@ TEST(OtaBootPackage, EmbeddedIdentityVectorCapsAndExplicitConfirmationGate) {
|
||||
OTA_BL_STORAGE_STAGE_CEILING | OTA_BL_STORAGE_BOOT_UPDATE,
|
||||
caps));
|
||||
|
||||
std::vector<uint8_t> full_only_caps(32, 0xFF);
|
||||
memcpy(full_only_caps.data() + 4, internal_marker, sizeof(internal_marker));
|
||||
full_only_caps[4 + 10] = 1;
|
||||
EXPECT_FALSE(ota_bootloader_caps_from_image(
|
||||
full_only_caps.data(), full_only_caps.size(),
|
||||
OTA_BL_STORAGE_STAGE_CEILING | OTA_BL_STORAGE_BOOT_UPDATE,
|
||||
caps));
|
||||
|
||||
OtaBlCaps selected;
|
||||
selected.present = true;
|
||||
selected.apply_abi = 3;
|
||||
@@ -270,7 +342,7 @@ TEST(OtaBootPackage, CapabilityScannerRejectsAnOtherwiseValidUnalignedMarker) {
|
||||
const uint8_t qspi_profile =
|
||||
OTA_BL_STORAGE_STAGE_CEILING | OTA_BL_STORAGE_QSPI |
|
||||
OTA_BL_STORAGE_BOOT_UPDATE;
|
||||
const uint8_t marker[16] = {'M','O','T','A','B','L','D','R', 3,0, 1,0,
|
||||
const uint8_t marker[16] = {'M','O','T','A','B','L','D','R', 3,0, 5,0,
|
||||
qspi_profile, 0,0,0};
|
||||
uint8_t image[64];
|
||||
memset(image, 0xFF, sizeof(image));
|
||||
|
||||
Reference in new issue
Block a user