Improve documentation

This commit is contained in:
Jan-Henrik 2022-04-19 11:51:32 +02:00
parent ae46dc50dd
commit ea8b062ad5
7 changed files with 89 additions and 57 deletions

View file

@ -6,7 +6,8 @@ Rust environments.
This crate contains implementations, macros and build.rs helpers for: This crate contains implementations, macros and build.rs helpers for:
* Partitioning of your memory into different sections * Partitioning of your memory into different sections
* Exchange of the contents of those partitions via the bootloader * Exchange of the contents of those partitions via the bootloader
* Signature-checking of the partitions contents with an algorithm of your choice * Signature/Checksum-checking of the partitions contents with an algorithm of your choice, because it is
done in firmware, not in bootloader
* Automatic Linker Script generation based on a Section/Parition Description in Rust Code * Automatic Linker Script generation based on a Section/Parition Description in Rust Code
## License ## License

View file

@ -11,6 +11,9 @@ use crate::log;
#[cfg(feature = "defmt")] #[cfg(feature = "defmt")]
use defmt::Format; use defmt::Format;
/// Error occured during nemory access
#[cfg_attr(feature = "use-defmt", derive(Format))] #[cfg_attr(feature = "use-defmt", derive(Format))]
#[derive(Debug)] #[derive(Debug)]
enum MemoryError { enum MemoryError {
@ -22,7 +25,7 @@ enum MemoryError {
/// Use this from your bootloader application and call boot() to do the magic, reading the current /// Use this from your bootloader application and call boot() to do the magic, reading the current
/// state via the State type and then jumping to the new image using the Jumper specified /// state via the State type and then jumping to the new image using the Jumper specified
pub struct MoonshineBoot< pub struct MoonbootBoot<
InternalMemory: Storage, InternalMemory: Storage,
HardwareState: State, HardwareState: State,
CPU: Processor, // TODO: Wrap these into a context struct like rubble? CPU: Processor, // TODO: Wrap these into a context struct like rubble?
@ -39,7 +42,7 @@ impl<
HardwareState: State, HardwareState: State,
CPU: Processor, CPU: Processor,
const INTERNAL_PAGE_SIZE: usize, const INTERNAL_PAGE_SIZE: usize,
> MoonshineBoot<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> > MoonbootBoot<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
{ {
/// create a new instance of the bootloader /// create a new instance of the bootloader
pub fn new( pub fn new(
@ -47,7 +50,7 @@ impl<
internal_memory: InternalMemory, internal_memory: InternalMemory,
state: HardwareState, state: HardwareState,
processor: CPU, processor: CPU,
) -> MoonshineBoot<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> { ) -> MoonbootBoot<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> {
Self { Self {
config, config,
internal_memory, internal_memory,

View file

@ -9,15 +9,18 @@ use desse::{Desse, DesseSized};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Identifier for multiple memory instances. Currently only Internal memory is supported
#[cfg_attr(feature = "defmt", derive(Format))] #[cfg_attr(feature = "defmt", derive(Format))]
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))] #[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))] #[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))]
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum MemoryUnit { pub enum MemoryUnit {
/// On-chip memory of your SoC
Internal, Internal,
// External(usize) // nth external unit // External(usize) // nth external unit
} }
/// Description of a memory bank in a specific memory unit
#[cfg_attr(feature = "defmt", derive(Format))] #[cfg_attr(feature = "defmt", derive(Format))]
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))] #[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))] #[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))]
@ -32,16 +35,17 @@ pub struct Bank {
pub memory_unit: MemoryUnit, pub memory_unit: MemoryUnit,
} }
/// Configuration of your SoCs partitioning
#[cfg_attr(feature = "defmt", derive(Format))] #[cfg_attr(feature = "defmt", derive(Format))]
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))] #[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))] #[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))]
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct Config { pub struct Config {
/// bank this bootloader jumps to /// bank this bootloader jumps to, holds your main firmware
pub boot_bank: Bank, pub boot_bank: Bank,
/// alternative bank we write the new firmware image to /// alternative bank we write the new firmware image to, used as a memory only
pub update_bank: Bank, pub update_bank: Bank,
/// bank the bootloader is contained in /// bank the bootloader is contained in, switching between banks
pub bootloader_bank: Bank, pub bootloader_bank: Bank,
// Initial Image is stored to this bank after first update, restore on failure // Initial Image is stored to this bank after first update, restore on failure
// pub golden_bank: Bank, // pub golden_bank: Bank,
@ -49,13 +53,17 @@ pub struct Config {
pub ram_bank: Bank, pub ram_bank: Bank,
} }
/// Configuration for linker scripts
#[cfg_attr(feature = "defmt", derive(Format))] #[cfg_attr(feature = "defmt", derive(Format))]
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))] #[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))] #[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))]
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct LinkerConfig { pub struct LinkerConfig {
/// Origin address of the internal non-volatile memory
pub flash_origin: Address, pub flash_origin: Address,
/// Origin address of the internal RAM bank
pub ram_origin: Address, pub ram_origin: Address,
// TODO enable via feature flag? // TODO enable via feature flag?
/// Whether to store the shared state in RAM and thus reserve some RAM memory for that
pub has_ram_state: bool, pub has_ram_state: bool,
} }

View file

@ -1,18 +1,25 @@
use crate::Address; use crate::Address;
// This trait executes an execution jump to the specified startign address. /// This trait executes an execution jump to the specified startign address.
// The implementation is ISA-dependent. /// The implementation is ISA-dependent.
pub trait Processor { pub trait Processor {
/// Jump to the image stored at the specified address. Never returns because it either
/// successfully switches to a new application image, or fails which leads to some
/// Hardfault/Panic/Whatever the Processor does then
fn do_jump(&mut self, address: Address) -> !; fn do_jump(&mut self, address: Address) -> !;
/// Setup the specified hardware config. Can be used to initialize an MPU for example.
fn setup(&mut self, config: &crate::hardware::Config); fn setup(&mut self, config: &crate::hardware::Config);
} }
/// Implementation of a processor based on the cortex-m crate
#[cfg(feature = "cortex-m")] #[cfg(feature = "cortex-m")]
mod cortex_m { mod cortex_m {
use super::Processor; use super::Processor;
/// cortex-m based [Processor]
pub struct CortexM {} pub struct CortexM {}
impl CortexM { impl CortexM {
/// Instantiate a new processor
pub fn new() -> Self { pub fn new() -> Self {
Self {} Self {}
} }
@ -35,5 +42,5 @@ mod cortex_m {
} }
#[cfg(feature = "cortex-m")] #[cfg(feature = "cortex-m")]
// A Jumper implementation for use with cortex-m processors /// A Jumper implementation for use with cortex-m processors
pub use cortex_m::CortexM; pub use cortex_m::CortexM;

View file

@ -6,24 +6,29 @@
//!This crate contains implementations, macros and build.rs helpers for: //!This crate contains implementations, macros and build.rs helpers for:
//!* Partitioning of your memory into different sections //!* Partitioning of your memory into different sections
//!* Exchange of the contents of those partitions via the bootloader //!* Exchange of the contents of those partitions via the bootloader
//!* Signature-checking of the partitions contents with an algorithm of your choice //!* Signature/Checksum-checking of the partitions contents with an algorithm of your choice, because it is
//!done in firmware, not in bootloader
//!* Automatic Linker Script generation based on a Section/Parition Description in Rust Code //!* Automatic Linker Script generation based on a Section/Parition Description in Rust Code
mod boot;
/// Implementations for use in the bootloader /// Implementations for use in the bootloader
pub mod boot; pub use boot::MoonbootBoot;
mod manager;
/// Implementations for use in the firmware
pub use manager::MoonbootManager;
/// Common hardware abstractions and associated implementations /// Common hardware abstractions and associated implementations
pub mod hardware; pub mod hardware;
/// Implementations for use in the firmware
pub mod manager;
/// Shared state management between firmware and bootloader /// Shared state management between firmware and bootloader
pub mod state; pub mod state;
pub use embedded_storage; pub use embedded_storage;
/// Because most of the time, ... // Because most of the time, ...
pub use boot as left_boot; //pub use boot::MoonbootBoot as LeftBoot;
/// ... there's two boots involved. // ... there's two boots involved.
pub use manager as right_boot; //pub use manager::MoonbootManager as RightBoot;
/// Address type in RAM or ROM /// Address type in RAM or ROM
pub type Address = u32; pub type Address = u32;

View file

@ -9,7 +9,7 @@ use crate::log;
/// Instantiate this in your application to enable mutation of the State specified in this and jump /// Instantiate this in your application to enable mutation of the State specified in this and jump
/// to the bootloader to apply any updates. /// to the bootloader to apply any updates.
pub struct MoonshineManager< pub struct MoonbootManager<
InternalMemory: Storage, InternalMemory: Storage,
HardwareState: State, HardwareState: State,
CPU: Processor, CPU: Processor,
@ -26,14 +26,14 @@ impl<
HardwareState: State, HardwareState: State,
CPU: Processor, CPU: Processor,
const INTERNAL_PAGE_SIZE: usize, const INTERNAL_PAGE_SIZE: usize,
> MoonshineManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> > MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
{ {
pub fn new( pub fn new(
config: Config, config: Config,
internal_memory: InternalMemory, internal_memory: InternalMemory,
state: HardwareState, state: HardwareState,
processor: CPU, processor: CPU,
) -> MoonshineManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> { ) -> MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> {
Self { Self {
config, config,
internal_memory, internal_memory,
@ -125,13 +125,14 @@ impl<
} }
} }
/// Easily get read access to the update bank
impl< impl<
InternalMemory: Storage, InternalMemory: Storage,
HardwareState: State, HardwareState: State,
CPU: Processor, CPU: Processor,
const INTERNAL_PAGE_SIZE: usize, const INTERNAL_PAGE_SIZE: usize,
> core::convert::AsRef<[u8]> > core::convert::AsRef<[u8]>
for MoonshineManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> for MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
{ {
#[inline] #[inline]
fn as_ref(&self) -> &[u8] { fn as_ref(&self) -> &[u8] {
@ -150,7 +151,7 @@ impl<
HardwareState: State, HardwareState: State,
CPU: Processor, CPU: Processor,
const INTERNAL_PAGE_SIZE: usize, const INTERNAL_PAGE_SIZE: usize,
> ReadStorage for MoonshineManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> > ReadStorage for MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
{ {
type Error = (); // TODO type Error = (); // TODO
@ -181,7 +182,7 @@ impl<
HardwareState: State, HardwareState: State,
CPU: Processor, CPU: Processor,
const INTERNAL_PAGE_SIZE: usize, const INTERNAL_PAGE_SIZE: usize,
> Storage for MoonshineManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> > Storage for MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
{ {
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
let bank = self.config.update_bank; // For now we always write updates to this bank. let bank = self.config.update_bank; // For now we always write updates to this bank.

View file

@ -9,7 +9,7 @@ use desse::{Desse, DesseSized};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
// Decision making for the bootloader /// Decision making states for the bootloader
// TODO: Hash + Signature? Should be done on download I think! This way, the algorithms can be // TODO: Hash + Signature? Should be done on download I think! This way, the algorithms can be
// exchanged via software updates easily // exchanged via software updates easily
#[cfg_attr(feature = "use-defmt", derive(Format))] #[cfg_attr(feature = "use-defmt", derive(Format))]
@ -35,54 +35,61 @@ pub enum Update {
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))] #[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))] #[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
// Errors that can occur during update /// Errors that can occur during update
pub enum UpdateError { pub enum UpdateError {
// A wrong Image Index has been specified /// A wrong Image Index has been specified
InvalidImageIndex, InvalidImageIndex,
// Failed to exchange the new image with the old one /// Failed to exchange the new image with the old one
ImageExchangeFailed, ImageExchangeFailed,
// Something f'ed up the internal state /// Something f'ed up the internal state
InvalidState, InvalidState,
// The Signature provided does not match the PublicKey or Image. /// The Signature provided does not match the PublicKey or Image.
InvalidSignature, InvalidSignature,
} }
// Store the progress of the current exchange operation /// Store the progress of the current exchange operation
#[cfg_attr(feature = "use-defmt", derive(Format))] #[cfg_attr(feature = "use-defmt", derive(Format))]
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))] #[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))] #[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExchangeProgress { pub struct ExchangeProgress {
// Bank the update is coming from /// Bank the update is coming from
pub(crate) a: Bank, pub(crate) a: Bank,
// Bank the update is going to /// Bank the update is going to
pub(crate) b: Bank, pub(crate) b: Bank,
// Page the operation has last copied /// Page the operation has last copied
pub(crate) page_index: u32, pub(crate) page_index: u32,
// Whether this exchange resulted from a Request (false) or a Revert (true) /// Whether this exchange resulted from a Request (false) or a Revert (true)
pub(crate) recovering: bool, pub(crate) recovering: bool,
} }
// Struct used to store the state of the bootloader situation in NVM /// Struct used to store the state of the bootloader situation in NVM
#[cfg_attr(feature = "use-defmt", derive(Format))] #[cfg_attr(feature = "use-defmt", derive(Format))]
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))] #[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))] #[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))]
#[derive(Debug)] #[derive(Debug)]
pub struct MoonshineState { pub struct MoonbootState {
// If set Request, an Update is requested. This will exchange the two images, set the update /// If set Request, an Update is requested. This will exchange the two images, set the update
// state to Revert and start the application. The application then has to set this state to /// state to Revert and start the application. The application then has to set this state to
// None and store it. If something went wrong and the boot attempt results in a restart of /// None and store it. If something went wrong and the boot attempt results in a restart of
// the bootloader, the bootloader starts with this variable set to Revert and thus exchanges /// the bootloader, the bootloader starts with this variable set to Revert and thus exchanges
// the two images again, doing a downgrade because of a failed boot /// the two images again, doing a downgrade because of a failed boot
pub update: Update, pub update: Update,
} }
/// Hardware abstraction for the state storage. Can for example be stored on a flash bank, or in
/// RAM. As long as you don't want to perform update download, power cycle the device, and then
/// apply the update, storing it in volatile memory is fine.
pub trait State { pub trait State {
fn read(&mut self) -> MoonshineState; /// Read the shared state
fn write(&mut self, data: MoonshineState) -> Result<(), ()>; fn read(&mut self) -> MoonbootState;
/// Write the new state to the shared state
fn write(&mut self, data: MoonbootState) -> Result<(), ()>;
} }
pub const STATE_SERIALIZED_MAX_SIZE: usize = MoonshineState::SIZE; /// Size of the serialized state
pub const STATE_SERIALIZED_MAX_SIZE: usize = MoonbootState::SIZE;
/// Type used to store the shared state CRC
pub type StateCrcType = u32; pub type StateCrcType = u32;
const CRC: Crc<StateCrcType> = Crc::<StateCrcType>::new(&CRC_32_CKSUM); const CRC: Crc<StateCrcType> = Crc::<StateCrcType>::new(&CRC_32_CKSUM);
@ -101,14 +108,14 @@ pub mod ram {
pub struct RamState; pub struct RamState;
extern "C" { extern "C" {
static mut _moonshine_state_crc_start: StateCrcType; static mut _moonboot_state_crc_start: StateCrcType;
static mut _moonshine_state_data_start: [u8; STATE_SERIALIZED_MAX_SIZE]; static mut _moonboot_state_data_start: [u8; STATE_SERIALIZED_MAX_SIZE];
// TODO: Move these as normal variables to linker sections via #[link] macro? // TODO: Move these as normal variables to linker sections via #[link] macro?
} }
impl State for RamState { impl State for RamState {
fn read(&mut self) -> MoonshineState { fn read(&mut self) -> MoonbootState {
let crc = unsafe { _moonshine_state_crc_start }; let crc = unsafe { _moonboot_state_crc_start };
log::info!( log::info!(
"Reading data with len: {}, CRC: {}", "Reading data with len: {}, CRC: {}",
@ -116,36 +123,36 @@ pub mod ram {
crc crc
); );
let checksum = checksum(unsafe { &_moonshine_state_data_start }); let checksum = checksum(unsafe { &_moonboot_state_data_start });
if crc == checksum { if crc == checksum {
let data = let data =
MoonshineState::deserialize_from(unsafe { &_moonshine_state_data_start }); MoonbootState::deserialize_from(unsafe { &_moonboot_state_data_start });
log::trace!("CRC Match! {}: {:?}", crc, data); log::trace!("CRC Match! {}: {:?}", crc, data);
return data; return data;
} else { } else {
log::trace!("CRC Mismatch! {} vs {}", crc, checksum); log::trace!("CRC Mismatch! {} vs {}", crc, checksum);
} }
MoonshineState { MoonbootState {
update: Update::None, update: Update::None,
} }
} }
fn write(&mut self, data: MoonshineState) -> Result<(), ()> { fn write(&mut self, data: MoonbootState) -> Result<(), ()> {
log::trace!("Writing data {:?}", data); log::trace!("Writing data {:?}", data);
unsafe { _moonshine_state_data_start = data.serialize() }; unsafe { _moonboot_state_data_start = data.serialize() };
log::trace!("Written data: {:?}", unsafe { log::trace!("Written data: {:?}", unsafe {
&_moonshine_state_data_start &_moonboot_state_data_start
}); });
unsafe { unsafe {
_moonshine_state_crc_start = checksum(&_moonshine_state_data_start); _moonboot_state_crc_start = checksum(&_moonboot_state_data_start);
} }
log::info!( log::info!(
"Written len: {}, checksum: {}", "Written len: {}, checksum: {}",
STATE_SERIALIZED_MAX_SIZE, STATE_SERIALIZED_MAX_SIZE,
unsafe { _moonshine_state_crc_start } unsafe { _moonboot_state_crc_start }
); );
Ok(()) Ok(())