From ea8b062ad5563670a33fec110fc08b93a5710c0a Mon Sep 17 00:00:00 2001 From: Jan-Henrik Bruhn Date: Tue, 19 Apr 2022 11:51:32 +0200 Subject: [PATCH] Improve documentation --- README.md | 3 +- src/boot/mod.rs | 9 +++-- src/hardware/mod.rs | 14 ++++++-- src/hardware/processor.rs | 13 +++++-- src/lib.rs | 21 ++++++----- src/manager/mod.rs | 13 +++---- src/state.rs | 73 +++++++++++++++++++++------------------ 7 files changed, 89 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 841c493..b85b394 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ Rust environments. This crate contains implementations, macros and build.rs helpers for: * Partitioning of your memory into different sections * 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 ## License diff --git a/src/boot/mod.rs b/src/boot/mod.rs index 48048f9..dad19e2 100644 --- a/src/boot/mod.rs +++ b/src/boot/mod.rs @@ -11,6 +11,9 @@ use crate::log; #[cfg(feature = "defmt")] use defmt::Format; + + +/// Error occured during nemory access #[cfg_attr(feature = "use-defmt", derive(Format))] #[derive(Debug)] enum MemoryError { @@ -22,7 +25,7 @@ enum MemoryError { /// 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 -pub struct MoonshineBoot< +pub struct MoonbootBoot< InternalMemory: Storage, HardwareState: State, CPU: Processor, // TODO: Wrap these into a context struct like rubble? @@ -39,7 +42,7 @@ impl< HardwareState: State, CPU: Processor, const INTERNAL_PAGE_SIZE: usize, - > MoonshineBoot + > MoonbootBoot { /// create a new instance of the bootloader pub fn new( @@ -47,7 +50,7 @@ impl< internal_memory: InternalMemory, state: HardwareState, processor: CPU, - ) -> MoonshineBoot { + ) -> MoonbootBoot { Self { config, internal_memory, diff --git a/src/hardware/mod.rs b/src/hardware/mod.rs index 30704a4..bdef5ce 100644 --- a/src/hardware/mod.rs +++ b/src/hardware/mod.rs @@ -9,15 +9,18 @@ use desse::{Desse, DesseSized}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +/// Identifier for multiple memory instances. Currently only Internal memory is supported #[cfg_attr(feature = "defmt", derive(Format))] #[cfg_attr(feature = "derive", derive(Serialize, Deserialize))] #[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))] #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum MemoryUnit { + /// On-chip memory of your SoC Internal, // External(usize) // nth external unit } +/// Description of a memory bank in a specific memory unit #[cfg_attr(feature = "defmt", derive(Format))] #[cfg_attr(feature = "derive", derive(Serialize, Deserialize))] #[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))] @@ -32,16 +35,17 @@ pub struct Bank { pub memory_unit: MemoryUnit, } +/// Configuration of your SoCs partitioning #[cfg_attr(feature = "defmt", derive(Format))] #[cfg_attr(feature = "derive", derive(Serialize, Deserialize))] #[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))] #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct Config { - /// bank this bootloader jumps to + /// bank this bootloader jumps to, holds your main firmware 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, - /// bank the bootloader is contained in + /// bank the bootloader is contained in, switching between banks pub bootloader_bank: Bank, // Initial Image is stored to this bank after first update, restore on failure // pub golden_bank: Bank, @@ -49,13 +53,17 @@ pub struct Config { pub ram_bank: Bank, } +/// Configuration for linker scripts #[cfg_attr(feature = "defmt", derive(Format))] #[cfg_attr(feature = "derive", derive(Serialize, Deserialize))] #[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))] #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct LinkerConfig { + /// Origin address of the internal non-volatile memory pub flash_origin: Address, + /// Origin address of the internal RAM bank pub ram_origin: Address, // 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, } diff --git a/src/hardware/processor.rs b/src/hardware/processor.rs index 762e7ff..e26cc45 100644 --- a/src/hardware/processor.rs +++ b/src/hardware/processor.rs @@ -1,18 +1,25 @@ use crate::Address; -// This trait executes an execution jump to the specified startign address. -// The implementation is ISA-dependent. +/// This trait executes an execution jump to the specified startign address. +/// The implementation is ISA-dependent. 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) -> !; + /// Setup the specified hardware config. Can be used to initialize an MPU for example. fn setup(&mut self, config: &crate::hardware::Config); } +/// Implementation of a processor based on the cortex-m crate #[cfg(feature = "cortex-m")] mod cortex_m { use super::Processor; + /// cortex-m based [Processor] pub struct CortexM {} impl CortexM { + /// Instantiate a new processor pub fn new() -> Self { Self {} } @@ -35,5 +42,5 @@ mod 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; diff --git a/src/lib.rs b/src/lib.rs index 62f8280..7ca2a85 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,24 +6,29 @@ //!This crate contains implementations, macros and build.rs helpers for: //!* Partitioning of your memory into different sections //!* 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 +mod boot; /// 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 pub mod hardware; -/// Implementations for use in the firmware -pub mod manager; /// Shared state management between firmware and bootloader pub mod state; pub use embedded_storage; -/// Because most of the time, ... -pub use boot as left_boot; -/// ... there's two boots involved. -pub use manager as right_boot; +// Because most of the time, ... +//pub use boot::MoonbootBoot as LeftBoot; +// ... there's two boots involved. +//pub use manager::MoonbootManager as RightBoot; /// Address type in RAM or ROM pub type Address = u32; diff --git a/src/manager/mod.rs b/src/manager/mod.rs index b0e2861..b6d514c 100644 --- a/src/manager/mod.rs +++ b/src/manager/mod.rs @@ -9,7 +9,7 @@ use crate::log; /// Instantiate this in your application to enable mutation of the State specified in this and jump /// to the bootloader to apply any updates. -pub struct MoonshineManager< +pub struct MoonbootManager< InternalMemory: Storage, HardwareState: State, CPU: Processor, @@ -26,14 +26,14 @@ impl< HardwareState: State, CPU: Processor, const INTERNAL_PAGE_SIZE: usize, - > MoonshineManager + > MoonbootManager { pub fn new( config: Config, internal_memory: InternalMemory, state: HardwareState, processor: CPU, - ) -> MoonshineManager { + ) -> MoonbootManager { Self { config, internal_memory, @@ -125,13 +125,14 @@ impl< } } +/// Easily get read access to the update bank impl< InternalMemory: Storage, HardwareState: State, CPU: Processor, const INTERNAL_PAGE_SIZE: usize, > core::convert::AsRef<[u8]> - for MoonshineManager + for MoonbootManager { #[inline] fn as_ref(&self) -> &[u8] { @@ -150,7 +151,7 @@ impl< HardwareState: State, CPU: Processor, const INTERNAL_PAGE_SIZE: usize, - > ReadStorage for MoonshineManager + > ReadStorage for MoonbootManager { type Error = (); // TODO @@ -181,7 +182,7 @@ impl< HardwareState: State, CPU: Processor, const INTERNAL_PAGE_SIZE: usize, - > Storage for MoonshineManager + > Storage for MoonbootManager { 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. diff --git a/src/state.rs b/src/state.rs index 291ae79..ae3cdae 100644 --- a/src/state.rs +++ b/src/state.rs @@ -9,7 +9,7 @@ use desse::{Desse, DesseSized}; #[cfg(feature = "serde")] 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 // exchanged via software updates easily #[cfg_attr(feature = "use-defmt", derive(Format))] @@ -35,54 +35,61 @@ pub enum Update { #[cfg_attr(feature = "derive", derive(Serialize, Deserialize))] #[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -// Errors that can occur during update +/// Errors that can occur during update pub enum UpdateError { - // A wrong Image Index has been specified + /// A wrong Image Index has been specified InvalidImageIndex, - // Failed to exchange the new image with the old one + /// Failed to exchange the new image with the old one ImageExchangeFailed, - // Something f'ed up the internal state + /// Something f'ed up the internal state InvalidState, - // The Signature provided does not match the PublicKey or Image. + /// The Signature provided does not match the PublicKey or Image. 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 = "derive", derive(Serialize, Deserialize))] #[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ExchangeProgress { - // Bank the update is coming from + /// Bank the update is coming from pub(crate) a: Bank, - // Bank the update is going to + /// Bank the update is going to pub(crate) b: Bank, - // Page the operation has last copied + /// Page the operation has last copied 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, } -// 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 = "derive", derive(Serialize, Deserialize))] #[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))] #[derive(Debug)] -pub struct MoonshineState { - // 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 - // 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 two images again, doing a downgrade because of a failed boot +pub struct MoonbootState { + /// 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 + /// 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 two images again, doing a downgrade because of a failed boot 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 { - fn read(&mut self) -> MoonshineState; - fn write(&mut self, data: MoonshineState) -> Result<(), ()>; + /// Read the shared state + 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; const CRC: Crc = Crc::::new(&CRC_32_CKSUM); @@ -101,14 +108,14 @@ pub mod ram { pub struct RamState; extern "C" { - static mut _moonshine_state_crc_start: StateCrcType; - static mut _moonshine_state_data_start: [u8; STATE_SERIALIZED_MAX_SIZE]; + static mut _moonboot_state_crc_start: StateCrcType; + static mut _moonboot_state_data_start: [u8; STATE_SERIALIZED_MAX_SIZE]; // TODO: Move these as normal variables to linker sections via #[link] macro? } impl State for RamState { - fn read(&mut self) -> MoonshineState { - let crc = unsafe { _moonshine_state_crc_start }; + fn read(&mut self) -> MoonbootState { + let crc = unsafe { _moonboot_state_crc_start }; log::info!( "Reading data with len: {}, CRC: {}", @@ -116,36 +123,36 @@ pub mod ram { crc ); - let checksum = checksum(unsafe { &_moonshine_state_data_start }); + let checksum = checksum(unsafe { &_moonboot_state_data_start }); if crc == checksum { let data = - MoonshineState::deserialize_from(unsafe { &_moonshine_state_data_start }); + MoonbootState::deserialize_from(unsafe { &_moonboot_state_data_start }); log::trace!("CRC Match! {}: {:?}", crc, data); return data; } else { log::trace!("CRC Mismatch! {} vs {}", crc, checksum); } - MoonshineState { + MoonbootState { update: Update::None, } } - fn write(&mut self, data: MoonshineState) -> Result<(), ()> { + fn write(&mut self, data: MoonbootState) -> Result<(), ()> { log::trace!("Writing data {:?}", data); - unsafe { _moonshine_state_data_start = data.serialize() }; + unsafe { _moonboot_state_data_start = data.serialize() }; log::trace!("Written data: {:?}", unsafe { - &_moonshine_state_data_start + &_moonboot_state_data_start }); unsafe { - _moonshine_state_crc_start = checksum(&_moonshine_state_data_start); + _moonboot_state_crc_start = checksum(&_moonboot_state_data_start); } log::info!( "Written len: {}, checksum: {}", STATE_SERIALIZED_MAX_SIZE, - unsafe { _moonshine_state_crc_start } + unsafe { _moonboot_state_crc_start } ); Ok(())