Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Releases up through `v0.4.0` were workspace-wide and tagged `vX.Y.Z`. From `0.4.
### Added

- Addressable devices for a shared bus. `boot::run` takes the bus addressing: `Addressable::new(chip_uid)` keeps the bootloader silent until a host selects it by the chip's 96-bit unique id, `Unaddressed` answers every frame as before. `app::new_addressable_app(ctl, wait_bytes)` does the same in the app. `chip_uid` reads the ESIG unique id, and `Usart` implements `Transport::wait_bytes` from its baud divisor.
- `boot_version!(compat = "name")` and `app_version!(compat = "name")` (re-exported from core): a bootloader with a compatibility key accepts only app images with the same key. The V00x and V103 examples set one.
- `boot_version!(compat = "name")` and `app_version!(compat = "name")` (re-exported from core): a bootloader with a compatibility key accepts only app images with the same key. The V00x and V103 examples set one. `boot` and `app` also re-export `compat_key` and `pack_version`, for an image that sets its own `version = ...`.
- The bootloader runs its USART at the baud stored in boot metadata, falling back to `UsartConfig::baud` when nothing is stored or `pclk` cannot reach the rate (divisor below 16). Divisors are computed at build time, so there is no runtime divide; the CH32V003 example bootloader grows by 64 bytes.
- `app::BaudRate` re-export for `App::set_baud` / `App::baud`.
- `boot::run_with_payload::<N>` runs the bootloader with an `N`-byte frame payload (at least 18; a Write may span flash pages); `boot::run` uses the 52-byte default.
Expand All @@ -43,7 +43,7 @@ Releases up through `v0.4.0` were workspace-wide and tagged `vX.Y.Z`. From `0.4.

- `addressing`: the `Addressing` trait, `Unaddressed`, and `Addressable::new(uid)` for devices sharing a bus. An addressable device answers `Select` when its id starts with the prefix, after a slot of 0 to 15 byte-times drawn from its id and the host's seed, and any other command only while selected. It never answers a frame it cannot read or another device's reply. `Dispatcher::with_addressing`, `Core::with_addressing` and `App::with_addressing` take it.
- `Transport::wait_bytes(n)`, a wait of about `n` byte-times for the `Select` slot; the default returns at once.
- Compatibility keys. `boot_version!` and `app_version!` take `compat = "name"` (hashed at compile time) or `compat_key = <u32>`, and end the image with a 6-byte tail, `ImageTail { compat, version }`. A bootloader with a key refuses `Verify` of an image with another key, or none, with `Status::Incompatible` and stays in `Updating`; without a key it carries no check (with LTO the unset key folds away).
- Compatibility keys. `boot_version!` and `app_version!` take `compat = "name"` (hashed at compile time) or `compat_key = <u32>`, and end the image with a 6-byte tail, `ImageTail { compat, version }`. A `version = <u16>` argument, alone or after the key, replaces the crate's `Cargo.toml` version; `pack_version` is re-exported to build one. A bootloader with a key refuses `Verify` of an image with another key, or none, with `Status::Incompatible` and stays in `Updating`; without a key it carries no check (with LTO the unset key folds away).
- `Reset` with `ResetFlags::BAUD` stores addr byte 0 as the bootloader's baud before resetting, in the bootloader and in `App::poll`.
- `App::baud` returns the stored bootloader baud; `App::set_baud` stores one, skipping the flash write when it is already stored.

Expand Down
2 changes: 1 addition & 1 deletion ch32/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ fn main() -> ! {

`Unaddressed` answers every frame, for a device that has the bus to itself. With several devices on one bus, pass `Addressable::new(chip_uid)` instead: the bootloader then stays silent until a host selects it by the chip's unique id (see the [shared bus guide](https://openservocore.github.io/tinyboot/shared-bus.html)).

`Storage` and `BootMetaStore` are initialized from linker symbols automatically. `boot_version!()` ends the image with its tail: a compatibility key and the crate's `Cargo.toml` version. `boot_version!(compat = "name")` sets the key, and the bootloader then accepts only app images built with the same `app_version!(compat = "name")` (see [app integration](https://openservocore.github.io/tinyboot/app-integration.html)).
`Storage` and `BootMetaStore` are initialized from linker symbols automatically. `boot_version!()` ends the image with its tail: a compatibility key and the crate's `Cargo.toml` version. `boot_version!(compat = "name")` sets the key, and the bootloader then accepts only app images built with the same `app_version!(compat = "name")`. A trailing `version = <u16>` replaces the crate version in either macro (see [app integration](https://openservocore.github.io/tinyboot/app-integration.html)).

`boot::run` reads frames with up to 52 payload bytes (a whole frame fits one 64-byte USB packet); `boot::run_with_payload::<N>` takes `N` bytes per Write (at least 18, not limited to a flash page) for faster updates, at `N` bytes of RAM. Hosts read the capacity from Info.

Expand Down
2 changes: 1 addition & 1 deletion ch32/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub use tinyboot_core::BaudRate;
pub use tinyboot_core::addressing::{Addressable, Addressing, Unaddressed};
pub use tinyboot_core::app::{App, AppConfig};
pub use tinyboot_core::traits;
pub use tinyboot_core::{app_version, pkg_version};
pub use tinyboot_core::{app_version, compat_key, pack_version, pkg_version};

/// Create an [`App`] configured for CH32 hardware.
pub fn new_app(ctl: BootCtl) -> App<BootCtl, BootMetaStore> {
Expand Down
2 changes: 1 addition & 1 deletion ch32/src/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub use crate::hal::{Pin, UsartMapping};
pub use tinyboot_core::Platform;
pub use tinyboot_core::addressing::{Addressable, Addressing, Unaddressed};
use tinyboot_core::frame::{DEFAULT_PAYLOAD, InfoData};
pub use tinyboot_core::{boot_version, pkg_version};
pub use tinyboot_core::{boot_version, compat_key, pack_version, pkg_version};

/// Common imports for bootloader binaries.
pub mod prelude {
Expand Down
9 changes: 9 additions & 0 deletions docs/app-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ tinyboot_ch32::app::app_version!(compat = "acme-widget-rev-b");

`compat = "name"` hashes the name at compile time (32-bit FNV-1a); `compat_key = 0x1234_5678` takes a raw value. tinyboot only compares keys, so encode whatever has to match: product, board revision, pinout. `Verify` refuses an image with another key, or none, with `Status::Incompatible` and leaves the device in its bootloader waiting for the right image; a mistaken image never starts. A bootloader built without a key accepts any image and carries no check.

The version in the tail is the crate's `Cargo.toml` version. A firmware with its own version number passes it after the key, or alone, packed 5.5.6 the way `tinyboot info` shows it:

```rust
tinyboot_ch32::app::app_version!(
compat = "acme-widget-rev-b",
version = tinyboot_ch32::app::pack_version(2, 4, 0)
);
```

## On a shared bus

Several devices on one bus need the addressable app, created with `new_addressable_app(BootCtl::new(), wait_bytes)`. It stays silent until the host selects it by the chip's unique id, like the addressable bootloader. `wait_bytes(n)` waits about `n` byte-times at the bus baud, and the example apps compute it from the USART's baud divisor. Build the bootloader addressable too. See the [shared bus guide](shared-bus.md).
Expand Down
57 changes: 33 additions & 24 deletions lib/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub use tinyboot_protocol::BaudRate;
pub use tinyboot_protocol::frame;
#[doc(hidden)]
pub use tinyboot_protocol::pkg_version;
pub use tinyboot_protocol::{COMPAT_UNSET, compat_key};
pub use tinyboot_protocol::{COMPAT_UNSET, compat_key, pack_version};

/// Last 6 bytes of a boot or app image, emitted by [`boot_version!`] or
/// [`app_version!`] into `.tb_version`. `tb-app.x` starts an app's tail
Expand Down Expand Up @@ -60,54 +60,63 @@ pub fn tinyboot_compat() -> u32 {
#[doc(hidden)]
#[macro_export]
macro_rules! __image_tail {
($compat:expr) => {
(@ $compat:expr, $version:expr) => {
#[unsafe(export_name = "__tb_tail")]
#[unsafe(link_section = ".tb_version")]
#[used]
static _TB_TAIL: $crate::ImageTail = $crate::ImageTail {
compat: $compat,
version: $crate::pkg_version!(),
version: $version,
};
};
() => {
$crate::__image_tail!(@ $crate::COMPAT_UNSET, $crate::pkg_version!());
};
(version = $version:expr) => {
$crate::__image_tail!(@ $crate::COMPAT_UNSET, $version);
};
(compat = $name:expr) => {
$crate::__image_tail!(@ $crate::compat_key($name), $crate::pkg_version!());
};
(compat = $name:expr, version = $version:expr) => {
$crate::__image_tail!(@ $crate::compat_key($name), $version);
};
(compat_key = $key:expr) => {
$crate::__image_tail!(@ $key, $crate::pkg_version!());
};
(compat_key = $key:expr, version = $version:expr) => {
$crate::__image_tail!(@ $key, $version);
};
}

/// Place the calling crate's version and a compatibility key into the
/// bootloader image's tail. Use once at module scope in a bootloader
/// binary.
/// Place a compatibility key and a version into the bootloader image's
/// tail. Use once at module scope in a bootloader binary.
///
/// - `boot_version!()`: no key; the bootloader accepts any image.
/// - `boot_version!(compat = "name")`: the key hashed from a name
/// ([`compat_key`](tinyboot_protocol::compat_key)).
/// - `boot_version!(compat_key = 0x1234_5678)`: a raw key.
///
/// The version is the calling crate's `Cargo.toml` version unless a
/// `version = <u16>` follows (alone or after the key), packed 5.5.6 as
/// [`pack_version`](tinyboot_protocol::pack_version) does.
///
/// With a key set, `Verify` refuses an image whose key differs with
/// `Status::Incompatible`.
#[macro_export]
macro_rules! boot_version {
() => {
$crate::__image_tail!($crate::COMPAT_UNSET);
};
(compat = $name:expr) => {
$crate::__image_tail!($crate::compat_key($name));
};
(compat_key = $key:expr) => {
$crate::__image_tail!($key);
($($args:tt)*) => {
$crate::__image_tail!($($args)*);
};
}

/// Place the calling crate's version and a compatibility key into the app
/// image's tail. Use once at module scope in an app binary. Arguments as
/// Place a compatibility key and a version into the app image's tail.
/// Use once at module scope in an app binary. Arguments as
/// [`boot_version!`]; the key must equal the bootloader's for it to
/// accept the image.
#[macro_export]
macro_rules! app_version {
() => {
$crate::__image_tail!($crate::COMPAT_UNSET);
};
(compat = $name:expr) => {
$crate::__image_tail!($crate::compat_key($name));
};
(compat_key = $key:expr) => {
$crate::__image_tail!($key);
($($args:tt)*) => {
$crate::__image_tail!($($args)*);
};
}
11 changes: 11 additions & 0 deletions lib/core/tests/tail_version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//! An image tail with its own version instead of the crate's.

use tinyboot_protocol::{compat_key, pack_version};

tinyboot_core::app_version!(compat = "board-a", version = pack_version(3, 1, 4));

#[test]
fn the_tail_carries_the_given_version_and_key() {
assert_eq!(tinyboot_core::tinyboot_version(), pack_version(3, 1, 4));
assert_eq!(tinyboot_core::tinyboot_compat(), compat_key("board-a"));
}
Loading