tsb: the protocol the tiers share is written once, and its bytes by name

tsb/tsb.hpp holds the link, the handshake and command bytes, the guard
policy and SPM interlock, the boot geometry, act_min/comm_window and a
consteval builder for the 16-byte info block, whose BUILDSTATE and ATmega
marker bytes are now named rather than commented. Every tier's image is
byte-identical to before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
BlackMarkandClaude Opus 5.5 committed 2026-10-24 12:24:12 +02:00
1 parent 44db419c4b
commit e395c0344f
5 files changed
+191 -293

No files matched your search

+106
View File
@@ -0,0 +1,106 @@
// The TinySafeBoot native-UART fixed-baud protocol, as every tier speaks it:
// the link, the handshake and command bytes, the boot geometry and the
// device-info block the host reads on activation.
#pragma once
#include <libavr/libavr.hpp>
namespace tsb {
using namespace avr::literals;
namespace spm = avr::spm;
namespace ee = avr::eeprom;
constexpr auto clock = 16_MHz;
constexpr auto baud_rate = 115200_Bd;
// Fixed 8N1; the library solves UBRR + U2X from clock and baud.
constexpr auto baud = avr::uart::solve_baud(clock, baud_rate, 8, avr::uart::parity::none);
// One bit time on the wire: the turn-around a shared-line peer needs to stop
// driving before this one starts.
constexpr auto guard_cycles = static_cast<std::uint32_t>(clock.hz / baud.actual);
// The loader is purely polled - it never enables interrupts - so every SPM and
// EEPROM lock folds to nothing under this posture.
constexpr auto off = avr::irq::guard_policy::unused;
// Strict request/response: every SPM operation is waited out before the next
// byte moves, so no flash operation is ever in flight at an EEPROM access -
// the write procedure's step 2 has nothing to guard, the omission the
// datasheet grants (DS40002061B section 8.6.3).
constexpr auto no_spm = ee::spm_interlock::omitted;
constexpr std::uint8_t confirm = '!';
constexpr std::uint8_t request = '?';
constexpr std::uint8_t knock = '@';
constexpr std::uint8_t activation_knocks = 3;
namespace command {
constexpr std::uint8_t read_flash = 'f';
constexpr std::uint8_t write_flash = 'F';
constexpr std::uint8_t read_eeprom = 'e';
constexpr std::uint8_t write_eeprom = 'E';
constexpr std::uint8_t read_config = 'c';
constexpr std::uint8_t write_config = 'C';
} // namespace command
// The lockout-proof floor for the receive window: the oracle's F_CPU/1MHz.
constexpr auto act_min = static_cast<std::uint8_t>(clock.hz / 1'000'000);
// Post-activation window: the host gets seconds, not milliseconds, mid-session.
constexpr std::uint8_t comm_window = 200;
constexpr std::uint16_t page = spm::page_bytes;
constexpr std::uint16_t eeprom_end = avr::hw::db.mem.eeprom_size - 1;
// The config page (TSB's LASTPAGE), one page below the boot section: the
// application's jump vector, then these.
consteval std::uint16_t config_page(std::uint16_t boot_bytes)
{
return spm::flash_bytes - boot_bytes - page;
}
constexpr std::uint8_t timeout_at = 2;
constexpr std::uint8_t password_at = 3;
// The firmware version stamp, in the encoding the host decodes.
consteval std::uint16_t date_stamp(std::uint16_t year, std::uint16_t month, std::uint16_t day)
{
return year * 512 + month * 32 + day;
}
// BUILDSTATE F3: the native-UART fixed-baud lineage.
constexpr std::uint8_t build_state = 0xF3;
constexpr std::uint8_t atmega_marker = 0xAA;
consteval auto info_block(std::uint16_t build_date, std::uint16_t app_end)
{
const auto low = [](std::uint16_t word) { return static_cast<std::uint8_t>(word); };
const auto high = [](std::uint16_t word) { return static_cast<std::uint8_t>(word >> 8); };
const std::uint16_t app_end_words = app_end / 2;
return std::to_array<std::uint8_t>({
'T',
'S',
'B',
low(build_date),
high(build_date),
build_state,
avr::hw::db.signature[0],
avr::hw::db.signature[1],
avr::hw::db.signature[2],
page / 2,
low(app_end_words),
high(app_end_words),
low(eeprom_end),
high(eeprom_end),
atmega_marker,
atmega_marker,
});
}
inline const std::uint8_t *flash_ptr(std::uint16_t addr)
{
return reinterpret_cast<const std::uint8_t *>(addr);
}
} // namespace tsb