mirror of
https://github.com/jhbruhn/moonboot.git
synced 2025-03-14 17:45:50 +00:00
Improve documentation
This commit is contained in:
parent
ae46dc50dd
commit
ea8b062ad5
7 changed files with 89 additions and 57 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
21
src/lib.rs
21
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;
|
||||
|
|
|
@ -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.
|
||||
|
|
73
src/state.rs
73
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<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(())
|
||||
|
|
Loading…
Reference in a new issue