Files
bootloader/test/pbapp.cpp
BlackMark 7d046c3b89 pureboot: PI lint, tightened gates, and the self-update test suite
check_pi.py asserts the two link-time facts position independence rests on
(no absolute jmp/call; the info block within the image's first 256 bytes);
the size gates drop to 510 on the tinies for the trampoline word.

New per-chip tests beside the reworked protocol test: the planner units
(programming orders and their recovery properties, the surgery, staging
composition, boot-fuse decode, and the update preflight's error/warning
matrix over synthetic fuse bytes), the relocated-copy sweep (the identical
image installed one slot lower serves the full command set — the PI
acceptance test, and the one that caught the temporary-buffer trap), and
the self-update end-to-end: --update-loader to a re-timed build
(pureboot9, byte-different by PUREBOOT_TIMEOUT alone), then every
power-fail phase killed mid-write, restarted from the runner's flash dump,
and completed by a re-run with the application intact throughout. The mega
rounds run the BOOTRST-unprogrammed profile: the fixture application's 'L'
jump is the application-owned loader entry that profile relies on.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 19:37:57 +02:00

68 lines
1.8 KiB
C++

// Test-fixture application for the pureboot protocol tests: prints "APP" on
// the chip's serial link (the same link the loader uses) — the proof that
// the loader's hand-over, and on the tinies the host's reset-vector
// surgery, actually launched it. Linked normally (crt, vectors at 0); on
// the tinies its reset vector is the rjmp the host re-homes.
//
// On the mega it then listens, and an 'L' makes it jump into the resident
// loader — the application-owned loader entry a BOOTRST-unprogrammed mega
// relies on (reset always boots the application there), exercised by the
// self-update tests. The tinies idle: reset reaches their loader through
// the patched vector, so the application owes it nothing.
#include <libavr/libavr.hpp>
using namespace avr::literals;
namespace {
consteval avr::hertz_t clock()
{
if (avr::hw::db.name == "ATtiny13A")
return 9.6_MHz;
if (avr::hw::db.name == "ATtiny85")
return 8_MHz;
return 16_MHz;
}
using dev = avr::device<{.clock = clock()}>;
template <avr::hertz_t C, bool Hardware = avr::hw::db.has_reg("UDR0")>
struct link {
using tx_t = avr::uart::usart0<C, {.baud = 115200_Bd, .max_baud_error = 2.5_pct}>;
static void tx(char c)
{
tx_t::write(static_cast<std::uint8_t>(c));
}
[[noreturn]] static void idle()
{
for (;;)
if (tx_t::read_blocking() == 'L')
reinterpret_cast<void (*)()>((avr::hw::db.mem.flash_size - 512) / 2)();
}
};
template <avr::hertz_t C>
struct link<C, false> {
using tx_t = avr::uart::software_tx<C, avr::pb1, 57600_Bd>;
static void tx(char c)
{
tx_t::write(static_cast<std::uint8_t>(c));
}
[[noreturn]] static void idle()
{
while (true) {
}
}
};
} // namespace
int main()
{
avr::init<typename link<dev::clock>::tx_t>();
link<dev::clock>::tx('A');
link<dev::clock>::tx('P');
link<dev::clock>::tx('P');
link<dev::clock>::idle();
}