Files
MeshCore/tools/mota/tests/migration_stubs/runtime.cpp
T

262 lines
8.1 KiB
C++

#include "Arduino.h"
#include "BootloaderMigrationResource.h"
#include "SHA256.h"
#include "helpers/ota/OtaSelf.h"
#include "nrf.h"
#include "nrf_mbr.h"
#include "nrf_sdm.h"
#include "nrf_soc.h"
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <stdexcept>
#if defined(_WIN32)
#include <windows.h>
#else
#include <sys/mman.h>
#endif
namespace migration_test {
uint32_t now = 0;
unsigned endf_checks = 0;
unsigned softdevice_checks = 0;
unsigned disables = 0;
unsigned resets = 0;
unsigned copies = 0;
bool candidate_installed = false;
void mbr_command() {
++copies;
throw std::runtime_error("unexpected MBR command in state-machine test");
}
} // namespace migration_test
// Include the actual driver. Only its target-specific naked SVC adapter is
// replaced: function-like 'volatile(...)' captures the extended assembly, but
// leaves ordinary 'volatile uint8_t *' memory reads unchanged. The adapter is
// an explicit failing trap, never a successful simulated flash operation.
#define __attribute__(...)
#define __asm
#define volatile(...) migration_test::mbr_command()
#include "helpers/ota/BootloaderMigration.cpp"
#undef volatile
#undef __asm
#undef __attribute__
const uint8_t migration_candidate[40960] = {};
const uint8_t migration_candidate_sha256[32] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };
const uint8_t migration_legacy_sha256[32] = {};
const bool migration_has_legacy = false;
const uint32_t migration_app_base = 0x26000, migration_boot_version = 0x02040CFF,
migration_board_id = 0x239A0029;
const uint16_t migration_softdevice_fwid = 0xB6;
const char migration_device_name[16] = "4631_DFU";
namespace mesh { namespace ota {
extern "C" {
uint32_t __flash_arduino_start[1] = {};
uint32_t __flash_arduino_end[1] = {};
}
} }
MigrationTestPower power_registers = {};
MigrationTestPower *NRF_POWER = &power_registers;
MigrationTestFicr *NRF_FICR = nullptr;
MigrationTestAcl *NRF_ACL = nullptr;
MigrationTestUicr *NRF_UICR = nullptr;
MigrationTestWdt *NRF_WDT = nullptr;
uint32_t millis() {
return migration_test::now;
}
void NVIC_SystemReset() {
++migration_test::resets;
throw std::runtime_error("unexpected reset in state-machine test");
}
uint32_t sd_softdevice_is_enabled(uint8_t *enabled) {
++migration_test::softdevice_checks;
*enabled = 0;
return NRF_SUCCESS;
}
uint32_t sd_softdevice_disable() {
++migration_test::disables;
throw std::runtime_error("unexpected SoftDevice disable in state-machine test");
}
uint32_t sd_power_gpregret_get(uint32_t, uint32_t *result) {
*result = 0;
return NRF_SUCCESS;
}
void SHA256::reset() {
}
void SHA256::update(const void *, size_t) {
}
void SHA256::finalize(void *out, size_t size) {
assert(size == 32);
// Scheduling tests exercise the full memory-read loop; cryptography itself
// is the production SHA256 library's contract, not this hardware stub.
std::memset(out, 0, size);
if (migration_test::candidate_installed)
std::memcpy(out, migration_candidate_sha256, size);
}
namespace mesh {
namespace ota {
bool ota_self_firmware(SelfFwInfo &) {
++migration_test::endf_checks;
return false; // Stop at the first install preflight, before hardware access.
}
} // namespace ota
} // namespace mesh
namespace {
using namespace mesh::ota;
const char confirmation[] = "0123456789ABCDEF";
char reply[160];
void arm() {
assert(bootloader_migration_request(confirmation, reply, sizeof(reply)));
assert(bootloader_migration_pending());
assert(std::strstr(reply, "OK migration armed"));
}
void no_install() {
assert(migration_test::endf_checks == 0);
assert(migration_test::softdevice_checks == 0);
assert(migration_test::disables == 0);
assert(migration_test::resets == 0);
assert(migration_test::copies == 0);
}
void status_result(unsigned result) {
bootloader_migration_status(reply, sizeof(reply));
char expected[24];
std::snprintf(expected, sizeof(expected), "last=%u", result);
assert(std::strstr(reply, expected));
}
} // namespace
int main(int argc, char **argv) {
assert(argc == 2);
using namespace migration_test;
#if defined(_WIN32)
assert(VirtualAlloc(reinterpret_cast<void *>(0xF0000), 0x10000,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) != nullptr);
#else
assert(mmap(reinterpret_cast<void *>(0xF0000), 0x10000, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) != MAP_FAILED);
#endif
const char *scenario = argv[1];
if (std::strcmp(scenario, "already-installed") == 0) {
candidate_installed = true;
assert(bootloader_migration_request(confirmation, reply, sizeof(reply)));
assert(!bootloader_migration_pending());
assert(std::strstr(reply, "already installed"));
now = 2000;
bootloader_migration_tick(true, true);
no_install();
bootloader_migration_status(reply, sizeof(reply));
assert(std::strstr(reply, "already installed"));
} else if (std::strcmp(scenario, "wrong-confirmation") == 0) {
assert(!bootloader_migration_request("0123456789ABCDE0", reply, sizeof(reply)));
assert(!bootloader_migration_request("0123456789abcdef", reply, sizeof(reply)));
assert(!bootloader_migration_request("0123456789ABCDEF ", reply, sizeof(reply)));
assert(!bootloader_migration_pending());
now = 2000;
bootloader_migration_tick(true, true);
no_install();
} else if (std::strcmp(scenario, "grace-and-drain") == 0) {
arm();
now = 1499;
bootloader_migration_tick(true, true);
no_install();
now = 1500;
bootloader_migration_tick(false, true);
no_install();
now = 29999;
bootloader_migration_tick(false, true);
no_install();
assert(bootloader_migration_pending());
bootloader_migration_tick(true, true);
assert(endf_checks == 1);
assert(!bootloader_migration_pending());
assert(disables == 0 && resets == 0 && copies == 0);
status_result(1);
} else if (std::strcmp(scenario, "duplicate-arm") == 0) {
arm();
now = 1499;
assert(!bootloader_migration_request(confirmation, reply, sizeof(reply)));
assert(std::strstr(reply, "already armed"));
now = 1500;
bootloader_migration_tick(true, true);
assert(endf_checks == 1); // A duplicate must not extend the grace period.
} else if (std::strcmp(scenario, "timeout") == 0) {
arm();
now = 30000;
bootloader_migration_tick(true, true);
assert(!bootloader_migration_pending());
no_install();
status_result(15);
bootloader_migration_tick(true, true);
assert(endf_checks == 0);
} else if (std::strcmp(scenario, "ota-cancel-rearm") == 0) {
arm();
now = 100;
bootloader_migration_tick(false, false);
assert(!bootloader_migration_pending());
no_install();
status_result(16);
now = 2000;
bootloader_migration_tick(true, true);
assert(endf_checks == 0); // Becoming idle must not revive cancelled work.
arm();
now = 3499;
bootloader_migration_tick(true, true);
assert(endf_checks == 0);
now = 3500;
bootloader_migration_tick(true, true);
assert(endf_checks == 1);
} else if (std::strcmp(scenario, "ota-cancel-at-install") == 0) {
arm();
now = 1500;
bootloader_migration_tick(true, false);
assert(!bootloader_migration_pending());
no_install();
status_result(16);
} else if (std::strcmp(scenario, "stale-retained-result") == 0) {
power_registers.GPREGRET = 0xCC;
status_result(12);
arm();
status_result(0);
now = 30000;
bootloader_migration_tick(true, true);
assert(!bootloader_migration_pending());
status_result(15);
arm();
status_result(0);
++now;
bootloader_migration_tick(true, false);
assert(!bootloader_migration_pending());
status_result(16);
assert(power_registers.GPREGRET == 0xCC);
assert(endf_checks == 0);
} else if (std::strcmp(scenario, "clock-wrap") == 0) {
now = UINT32_MAX - 999u;
arm();
now += 1499u;
bootloader_migration_tick(true, true);
no_install();
++now;
bootloader_migration_tick(true, true);
assert(endf_checks == 1);
} else {
throw std::runtime_error("unknown scenario");
}
assert(disables == 0 && resets == 0 && copies == 0);
}