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:
1 parent
44db419c4b
commit
e395c0344f
5 files changed
+191
-293
No files matched your search
+106
@@ -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
|
||||
Reference in new issue
Block a user