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:
* 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

View file

@ -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<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
> MoonbootBoot<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
{
/// create a new instance of the bootloader
pub fn new(
@ -47,7 +50,7 @@ impl<
internal_memory: InternalMemory,
state: HardwareState,
processor: CPU,
) -> MoonshineBoot<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> {
) -> MoonbootBoot<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> {
Self {
config,
internal_memory,

View file

@ -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,
}

View file

@ -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;

View file

@ -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;

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
/// 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<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
> MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
{
pub fn new(
config: Config,
internal_memory: InternalMemory,
state: HardwareState,
processor: CPU,
) -> MoonshineManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> {
) -> MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> {
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<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
for MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
{
#[inline]
fn as_ref(&self) -> &[u8] {
@ -150,7 +151,7 @@ impl<
HardwareState: State,
CPU: Processor,
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
@ -181,7 +182,7 @@ impl<
HardwareState: State,
CPU: Processor,
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> {
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")]
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<StateCrcType> = Crc::<StateCrcType>::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(())