#!/usr/bin/env python3 """Run the embedded bridge's real geometry and bootloader identity policy.""" import os from pathlib import Path import subprocess import tempfile import unittest ROOT = Path(__file__).resolve().parents[2] STUBS = ROOT / 'tools/mota/tests/migration_stubs' HARNESS = r''' #include #include #include #include "helpers/ota/BootloaderMigrationPolicy.h" using namespace mesh::ota; using namespace mesh::ota::migration; static void wr16(uint8_t *p, uint16_t n) { p[0]=n; p[1]=n>>8; } static void wr32(uint8_t *p, uint32_t n) { wr16(p,n); wr16(p+2,n>>16); } static OtaBootloaderIdentity identity(uint32_t version, uint32_t base=0x26000, uint16_t fwid=0xB6) { std::array bytes; bytes.fill(0xFF); uint8_t *p=bytes.data()+OTA_BOOT_CANDIDATE_MANIFEST_OFFSET; wr32(p,OTA_BOOT_MANIFEST_MAGIC0); wr32(p+4,OTA_BOOT_MANIFEST_MAGIC1); wr16(p+8,1); wr16(p+10,44); wr32(p+12,BOOT_BASE); wr32(p+16,BOOT_SIZE); wr32(p+20,0x239A0029); std::memset(p+24,0,16); std::memcpy(p+24,"4631_DFU",8); wr32(p+44,OTA_BOOT_CONTINUITY_MAGIC0); wr32(p+48,OTA_BOOT_CONTINUITY_MAGIC1); wr16(p+52,2); wr16(p+54,32); wr32(p+56,version); wr16(p+60,140); wr16(p+62,fwid); wr32(p+64,base); wr16(p+68,1); wr16(p+70,0); wr32(p+72,0); wr32(p+40,ota_boot_image_crc32(bytes.data(),bytes.size(),OTA_BOOT_CANDIDATE_MANIFEST_OFFSET+40)); OtaBootloaderIdentity out; assert(ota_bootloader_candidate_identity_from_image(bytes.data(),bytes.size(),out)); bytes[10]^=1; OtaBootloaderIdentity corrupt; assert(!ota_bootloader_candidate_identity_from_image(bytes.data(),bytes.size(),corrupt)); return out; } static Snapshot good() { Snapshot s{}; s.part=0x52840; s.flash_page_size=4096; s.flash_pages=256; s.app_base=0x26000; s.app_ceiling=0xED000; s.softdevice_size=s.app_base; s.softdevice_fwid=s.expected_softdevice_fwid=0xB6; s.mbr_boot_override=s.mbr_params_override=UINT32_MAX; s.uicr_boot=BOOT_BASE; s.uicr_params=PARAMS_BASE; s.source=0xA0000; s.source_size=BOOT_SIZE; s.app_body_size=0x90000; s.app_image_size=s.app_body_size+ENDF_LEN; return s; } static std::array labelled(const char *text, size_t offset=100) { std::array bytes; bytes.fill(0xFF); assert(offset + std::strlen(text) + 1 <= bytes.size()); std::memcpy(bytes.data() + offset, text, std::strlen(text) + 1); return bytes; } static InstalledVariant variant(const std::array &image, uint32_t version=0x02040CFF) { return installed_variant(image.data(), image.size(), version); } int main(int argc,char **argv) { assert(argc==2); if (!std::strcmp(argv[1],"normal-labels")) { assert(variant(labelled("UF2 Bootloader v0.11.0-OTAFIX2.4.12\r\nModel: test\r\n")) == InstalledVariant::Normal); assert(variant(labelled("UF2 Bootloader 0.11.0-OTAFIX2.4.12\r\n")) == InstalledVariant::Normal); assert(variant(labelled("UF2 Bootloader BL_0x02040CFF\r\n")) == InstalledVariant::Normal); assert(variant(labelled("UF2 Bootloader v0.11.0-OTAFIX2.4.11\r\n"),0x02040BFF) == InstalledVariant::Normal); assert(variant(labelled("UF2 Bootloader v0.11.0-OTAFIX2.4.12-preview.16\r\n"),0x02040C10) == InstalledVariant::Normal); assert(variant(labelled("UF2 Bootloader BL_0x02040C10\r\n"),0x02040C10) == InstalledVariant::Normal); assert(variant(labelled("UF2 Bootloader v0.11.0-OTAFIX255.255.254-preview.254\r\n"),0xFFFFFEFE) == InstalledVariant::Normal); } else if (!std::strcmp(argv[1],"recovery-labels")) { for (const char *text: {"R_0x02040CFF", "UF2 Bootloader R_0x02040CFF\r\n", "UF2 Bootloader R_v0.11.0-OTAFIX2.4.12\r\n", "0.11.0-OTAFIX2.4.7-preview.2-recovery-allow-all s140 6.1.1"}) { auto bytes=labelled("UF2 Bootloader v0.11.0-OTAFIX2.4.12\r\n"); std::memcpy(bytes.data()+500,text,std::strlen(text)+1); assert(variant(bytes)==InstalledVariant::Recovery); // Recovery wins over a normal decoy. assert(variant(labelled(text))==InstalledVariant::Recovery); } // Historical compact recovery images have neither version label nor flag. assert(variant(labelled(""))==InstalledVariant::Unknown); auto bytes=labelled("UF2 Bootloader v0.11.0-OTAFIX2.4.12\r\n"); std::memcpy(bytes.data()+BOOT_SIZE-4,"R_0x",4); assert(variant(bytes)==InstalledVariant::Recovery); } else if (!std::strcmp(argv[1],"unknown-labels")) { for (const char *text: {"", "v0.11.0-OTAFIX2.4.12", "UF2 Bootloader ", "UF2 Bootloader v0.11.0-OTAFIX2.4.11\r\n", "UF2 Bootloader v0.11.0-OTAFIX2.4.12-preview.1\r\n", "UF2 Bootloader v0.11.0-OTAFIX2.4.12-dirty\r\n", "UF2 Bootloader v0.11.0-OTAFIX2.4.12\n", "UF2 Bootloader v0.11.0-OTAFIX2.4.12", "UF2 Bootloader vBL_0x02040CFF\r\n", "UF2 Bootloader BL_0x02040DFF\r\n"}) assert(variant(labelled(text))==InstalledVariant::Unknown); auto bytes=labelled("UF2 Bootloader v0.11.0-OTAFIX2.4.12\r\n"); assert(installed_variant(nullptr,BOOT_SIZE,0x02040CFF)==InstalledVariant::Unknown); assert(installed_variant(bytes.data(),BOOT_SIZE-1,0x02040CFF)==InstalledVariant::Unknown); assert(variant(bytes,0)==InstalledVariant::Unknown); assert(variant(bytes,UINT32_MAX)==InstalledVariant::Unknown); assert(variant(bytes,0x02040C00)==InstalledVariant::Unknown); const char *duplicate="UF2 Bootloader BL_0x02040CFF\r\n"; std::memcpy(bytes.data()+500,duplicate,std::strlen(duplicate)+1); assert(variant(bytes)==InstalledVariant::Unknown); bytes=labelled("UF2 Bootloader v0.11.0-OTAFIX2.4.12\r\n"); const char *conflict="UF2 Bootloader v0.11.0-OTAFIX2.4.11\r\n"; std::memcpy(bytes.data()+500,conflict,std::strlen(conflict)+1); assert(variant(bytes)==InstalledVariant::Unknown); for (size_t tail=1;tail<40;++tail) { bytes.fill(0xFF); const char *label="UF2 Bootloader v0.11.0-OTAFIX2.4.12\r\n"; size_t count=std::strlen(label); if (tail>=count) continue; std::memcpy(bytes.data()+BOOT_SIZE-tail,label,tail); assert(variant(bytes)==InstalledVariant::Unknown); } } else if (!std::strcmp(argv[1],"geometry")) { assert(preflight(good())==Gate::Ok); Snapshot s=good(); s.part=0x52833; assert(preflight(s)==Gate::Chip); s=good(); s.flash_page_size=2048; assert(preflight(s)==Gate::Chip); s=good(); s.flash_pages=512; assert(preflight(s)==Gate::Chip); s=good(); s.app_base=s.softdevice_size=0x27000; s.softdevice_fwid=s.expected_softdevice_fwid=0x123; assert(preflight(s)==Gate::Ok); s.expected_softdevice_fwid=0xB6; assert(preflight(s)==Gate::Softdevice); s=good(); s.softdevice_size=0x27000; assert(preflight(s)==Gate::Softdevice); s=good(); s.app_ceiling=0xEE000; assert(preflight(s)==Gate::Softdevice); s=good(); s.app_ceiling=s.app_base; assert(preflight(s)==Gate::Softdevice); s=good(); s.uicr_boot=0xF0000; assert(preflight(s)==Gate::BootAddress); s=good(); s.mbr_boot_override=BOOT_BASE; s.uicr_boot=0; assert(preflight(s)==Gate::Ok); s=good(); s.mbr_boot_override=0xF0000; assert(preflight(s)==Gate::BootAddress); s=good(); s.uicr_params=UINT32_MAX; assert(preflight(s)==Gate::ParamsAddress); s=good(); s.mbr_params_override=SETTINGS_BASE; assert(preflight(s)==Gate::ParamsAddress); const uint32_t bad[]={0,0x1000,0x25FFC,0xED000,BOOT_BASE,PARAMS_BASE,0x20000000,UINT32_MAX-3}; for (uint32_t address:bad) { s=good(); s.source=address; assert(preflight(s)==Gate::Source); } s=good(); ++s.source; assert(preflight(s)==Gate::Source); s=good(); --s.source_size; assert(preflight(s)==Gate::Source); s=good(); s.app_body_size=s.source-s.app_base+BOOT_SIZE-1; assert(preflight(s)==Gate::Source); s=good(); s.app_body_size=s.app_ceiling-s.app_base+1; assert(preflight(s)==Gate::Source); s=good(); s.app_body_size=s.app_ceiling-s.app_base; s.app_image_size=s.app_body_size+ENDF_LEN; assert(preflight(s)==Gate::Source); // EndF must fit as well as the body. s=good(); --s.app_image_size; assert(preflight(s)==Gate::Source); s=good(); s.app_ceiling=0xD4000; s.app_body_size=0xAF000; assert(preflight(s)==Gate::Source); } else if (!std::strcmp(argv[1],"acl")) { for (unsigned slot=0;slot<8;++slot) { for (uint32_t address=BOOT_BASE;address