mirror of
https://github.com/jhbruhn/moonboot.git
synced 2025-03-15 01:55:50 +00:00
Refactored swap/scratch to state
This commit is contained in:
parent
01fe251f1f
commit
e2d1ce797b
7 changed files with 136 additions and 135 deletions
|
@ -1,8 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
hardware::processor::Processor,
|
hardware::processor::Processor,
|
||||||
hardware::{Bank, Config},
|
hardware::{Bank, Config},
|
||||||
state::{ExchangeProgress, ExchangeStep, State, Update, UpdateError},
|
state::{Exchange, ExchangeProgress, ExchangeStep, MemoryError, State, Update, UpdateError},
|
||||||
swap::{MemoryError, Swap},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use embedded_storage::Storage;
|
use embedded_storage::Storage;
|
||||||
|
@ -15,47 +14,47 @@ use defmt::Format;
|
||||||
/// 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 MoonbootBoot<
|
pub struct MoonbootBoot<
|
||||||
InternalMemory: Storage,
|
STORAGE: Storage,
|
||||||
HardwareState: State,
|
STATE: State,
|
||||||
CPU: Processor, // TODO: Wrap these into a context struct like rubble?
|
PROCESSOR: Processor, // TODO: Wrap these into a context struct like rubble?
|
||||||
PageSwap: Swap,
|
EXCHANGE: Exchange,
|
||||||
const INTERNAL_PAGE_SIZE: usize,
|
const INTERNAL_PAGE_SIZE: usize,
|
||||||
> {
|
> {
|
||||||
config: Config,
|
config: Config,
|
||||||
internal_memory: InternalMemory,
|
storage: STORAGE,
|
||||||
state: HardwareState,
|
state: STATE,
|
||||||
processor: CPU,
|
processor: PROCESSOR,
|
||||||
swap: PageSwap,
|
exchange: EXCHANGE,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
InternalMemory: Storage,
|
STORAGE: Storage,
|
||||||
HardwareState: State,
|
STATE: State,
|
||||||
CPU: Processor,
|
PROCESSOR: Processor,
|
||||||
PageSwap: Swap,
|
EXCHANGE: Exchange,
|
||||||
const INTERNAL_PAGE_SIZE: usize,
|
const INTERNAL_PAGE_SIZE: usize,
|
||||||
> MoonbootBoot<InternalMemory, HardwareState, CPU, PageSwap, INTERNAL_PAGE_SIZE>
|
> MoonbootBoot<STORAGE, STATE, PROCESSOR, EXCHANGE, INTERNAL_PAGE_SIZE>
|
||||||
{
|
{
|
||||||
/// create a new instance of the bootloader
|
/// create a new instance of the bootloader
|
||||||
pub fn new(
|
pub fn new(
|
||||||
config: Config,
|
config: Config,
|
||||||
internal_memory: InternalMemory,
|
storage: STORAGE,
|
||||||
state: HardwareState,
|
state: STATE,
|
||||||
processor: CPU,
|
processor: PROCESSOR,
|
||||||
swap: PageSwap,
|
exchange: EXCHANGE,
|
||||||
) -> MoonbootBoot<InternalMemory, HardwareState, CPU, PageSwap, INTERNAL_PAGE_SIZE> {
|
) -> MoonbootBoot<STORAGE, STATE, PROCESSOR, EXCHANGE, INTERNAL_PAGE_SIZE> {
|
||||||
Self {
|
Self {
|
||||||
config,
|
config,
|
||||||
internal_memory,
|
storage,
|
||||||
state,
|
state,
|
||||||
processor,
|
processor,
|
||||||
swap,
|
exchange,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destroy this instance of the bootloader and return access to the hardware peripheral
|
/// Destroy this instance of the bootloader and return access to the hardware peripheral
|
||||||
pub fn destroy(self) -> (InternalMemory, HardwareState, CPU) {
|
pub fn destroy(self) -> (STORAGE, STATE, PROCESSOR) {
|
||||||
(self.internal_memory, self.state, self.processor)
|
(self.storage, self.state, self.processor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the update and boot logic of the bootloader
|
/// Execute the update and boot logic of the bootloader
|
||||||
|
@ -124,9 +123,9 @@ impl<
|
||||||
);
|
);
|
||||||
|
|
||||||
let exchange_result = self
|
let exchange_result = self
|
||||||
.swap
|
.exchange
|
||||||
.exchange::<InternalMemory, HardwareState, INTERNAL_PAGE_SIZE>(
|
.exchange::<STORAGE, STATE, INTERNAL_PAGE_SIZE>(
|
||||||
&mut self.internal_memory,
|
&mut self.storage,
|
||||||
&mut self.state,
|
&mut self.state,
|
||||||
progress,
|
progress,
|
||||||
);
|
);
|
||||||
|
@ -165,7 +164,7 @@ impl<
|
||||||
old.location,
|
old.location,
|
||||||
old.size / 1024,
|
old.size / 1024,
|
||||||
new.location,
|
new.location,
|
||||||
new.size / 1024,
|
new.size / 1024
|
||||||
);
|
);
|
||||||
|
|
||||||
// Try to exchange the firmware images
|
// Try to exchange the firmware images
|
||||||
|
@ -195,9 +194,9 @@ impl<
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exchange_banks(&mut self, a: Bank, b: Bank) -> Result<(), MemoryError> {
|
fn exchange_banks(&mut self, a: Bank, b: Bank) -> Result<(), MemoryError> {
|
||||||
self.swap
|
self.exchange
|
||||||
.exchange::<InternalMemory, HardwareState, INTERNAL_PAGE_SIZE>(
|
.exchange::<STORAGE, STATE, INTERNAL_PAGE_SIZE>(
|
||||||
&mut self.internal_memory,
|
&mut self.storage,
|
||||||
&mut self.state,
|
&mut self.state,
|
||||||
ExchangeProgress {
|
ExchangeProgress {
|
||||||
a,
|
a,
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub mod cortex_m {
|
||||||
fn do_jump(&mut self, address: super::Address) -> ! {
|
fn do_jump(&mut self, address: super::Address) -> ! {
|
||||||
unsafe {
|
unsafe {
|
||||||
// Set Vector Table to new vector table (unsafe but okay here)
|
// Set Vector Table to new vector table (unsafe but okay here)
|
||||||
(*cortex_m::peripheral::SCB::ptr()).vtor.write(address);
|
(*cortex_m::peripheral::SCB::PTR).vtor.write(address);
|
||||||
|
|
||||||
cortex_m::asm::bootload(address as *const u32);
|
cortex_m::asm::bootload(address as *const u32);
|
||||||
}
|
}
|
||||||
|
@ -40,4 +40,3 @@ pub mod cortex_m {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,6 @@ pub use manager::MoonbootManager;
|
||||||
pub mod hardware;
|
pub mod hardware;
|
||||||
/// Shared state management between firmware and bootloader
|
/// Shared state management between firmware and bootloader
|
||||||
pub mod state;
|
pub mod state;
|
||||||
/// Abstractions on the method of flash page swapping.
|
|
||||||
pub mod swap;
|
|
||||||
|
|
||||||
pub use embedded_storage;
|
pub use embedded_storage;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,37 @@
|
||||||
|
#[cfg(feature = "ram-state")]
|
||||||
|
pub mod ram;
|
||||||
|
|
||||||
|
#[cfg(feature = "scratch-state")]
|
||||||
|
pub mod scratch;
|
||||||
|
|
||||||
|
use embedded_storage::Storage;
|
||||||
|
|
||||||
|
/// Error occured during memory access
|
||||||
|
#[cfg_attr(feature = "use-defmt", derive(Format))]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MemoryError {
|
||||||
|
BankSizeNotEqual,
|
||||||
|
BankSizeZero,
|
||||||
|
ReadFailure,
|
||||||
|
WriteFailure,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Abstraction for the exchange operation of the current state.
|
||||||
|
pub trait Exchange {
|
||||||
|
fn exchange<STORAGE: Storage, STATE: State, const INTERNAL_PAGE_SIZE: usize>(
|
||||||
|
&mut self,
|
||||||
|
internal_memory: &mut STORAGE,
|
||||||
|
state: &mut STATE,
|
||||||
|
progress: ExchangeProgress,
|
||||||
|
) -> Result<(), MemoryError>;
|
||||||
|
}
|
||||||
|
|
||||||
use crate::hardware::Bank;
|
use crate::hardware::Bank;
|
||||||
use crate::log;
|
|
||||||
|
|
||||||
use crc::{Crc, CRC_32_CKSUM};
|
use crc::{Crc, CRC_32_CKSUM};
|
||||||
#[cfg(feature = "defmt")]
|
#[cfg(feature = "defmt")]
|
||||||
use defmt::Format;
|
use defmt::Format;
|
||||||
#[cfg(feature = "ram-state")]
|
#[cfg(any(feature = "ram-state", feature = "scratch-state"))]
|
||||||
use desse::{Desse, DesseSized};
|
use desse::{Desse, DesseSized};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -14,7 +41,10 @@ use serde::{Deserialize, Serialize};
|
||||||
// exchanged via software updates easily
|
// exchanged via software updates easily
|
||||||
#[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(
|
||||||
|
any(feature = "ram-state", feature = "scratch-state"),
|
||||||
|
derive(Desse, DesseSized)
|
||||||
|
)]
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum Update {
|
pub enum Update {
|
||||||
// No update requested, just jump to the application
|
// No update requested, just jump to the application
|
||||||
|
@ -33,7 +63,10 @@ pub enum Update {
|
||||||
|
|
||||||
#[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(
|
||||||
|
any(feature = "ram-state", feature = "scratch-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 {
|
||||||
|
@ -50,7 +83,10 @@ pub enum UpdateError {
|
||||||
/// Upcoming operation of the exchange process for a given page index.
|
/// Upcoming operation of the exchange process for a given page index.
|
||||||
#[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(
|
||||||
|
any(feature = "ram-state", feature = "scratch-state"),
|
||||||
|
derive(Desse, DesseSized)
|
||||||
|
)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum ExchangeStep {
|
pub enum ExchangeStep {
|
||||||
/// Copy page A to the scratch page.
|
/// Copy page A to the scratch page.
|
||||||
|
@ -64,7 +100,10 @@ pub enum ExchangeStep {
|
||||||
/// 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(
|
||||||
|
any(feature = "ram-state", feature = "scratch-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
|
||||||
|
@ -82,7 +121,10 @@ pub struct ExchangeProgress {
|
||||||
/// 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(
|
||||||
|
any(feature = "ram-state", feature = "scratch-state"),
|
||||||
|
derive(Desse, DesseSized)
|
||||||
|
)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MoonbootState {
|
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
|
||||||
|
@ -112,63 +154,3 @@ const CRC: Crc<StateCrcType> = Crc::<StateCrcType>::new(&CRC_32_CKSUM);
|
||||||
fn checksum(bytes: &[u8]) -> StateCrcType {
|
fn checksum(bytes: &[u8]) -> StateCrcType {
|
||||||
CRC.checksum(bytes)
|
CRC.checksum(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// State stored in the RAM
|
|
||||||
/// TODO: Move to hardware folder together with state trait?
|
|
||||||
#[cfg(feature = "ram-state")]
|
|
||||||
pub mod ram {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
/// State read and written to RAM. This assumes the device is never powered off / the ram is never
|
|
||||||
/// reset!
|
|
||||||
pub struct RamState;
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
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) -> MoonbootState {
|
|
||||||
let crc = unsafe { _moonboot_state_crc_start };
|
|
||||||
|
|
||||||
log::info!(
|
|
||||||
"Reading data with len: {}, CRC: {}",
|
|
||||||
STATE_SERIALIZED_MAX_SIZE,
|
|
||||||
crc
|
|
||||||
);
|
|
||||||
|
|
||||||
let checksum = checksum(unsafe { &_moonboot_state_data_start });
|
|
||||||
if crc == checksum {
|
|
||||||
let data = MoonbootState::deserialize_from(unsafe { &_moonboot_state_data_start });
|
|
||||||
log::trace!("CRC Match! {}: {:?}", crc, data);
|
|
||||||
return data;
|
|
||||||
} else {
|
|
||||||
log::trace!("CRC Mismatch! {} vs {}", crc, checksum);
|
|
||||||
}
|
|
||||||
|
|
||||||
MoonbootState {
|
|
||||||
update: Update::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, data: &MoonbootState) -> Result<(), ()> {
|
|
||||||
log::trace!("Writing data {:?}", data);
|
|
||||||
|
|
||||||
unsafe { _moonboot_state_data_start = data.serialize() };
|
|
||||||
log::trace!("Written data: {:?}", unsafe { &_moonboot_state_data_start });
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
_moonboot_state_crc_start = checksum(&_moonboot_state_data_start);
|
|
||||||
}
|
|
||||||
log::info!(
|
|
||||||
"Written len: {}, checksum: {}",
|
|
||||||
STATE_SERIALIZED_MAX_SIZE,
|
|
||||||
unsafe { _moonboot_state_crc_start }
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +1,63 @@
|
||||||
use embedded_storage::Storage;
|
use embedded_storage::Storage;
|
||||||
|
|
||||||
use crate::{
|
use crate::{log, Address};
|
||||||
log,
|
|
||||||
state::{ExchangeProgress, State, Update},
|
|
||||||
swap::{MemoryError, Swap},
|
|
||||||
Address,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Ram;
|
use super::*;
|
||||||
|
|
||||||
impl Swap for Ram {
|
/// State read and written to RAM. This assumes the device is never powered off / the ram is never
|
||||||
|
/// reset!
|
||||||
|
pub struct RamState;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
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) -> MoonbootState {
|
||||||
|
let crc = unsafe { _moonboot_state_crc_start };
|
||||||
|
|
||||||
|
log::info!(
|
||||||
|
"Reading data with len: {}, CRC: {}",
|
||||||
|
STATE_SERIALIZED_MAX_SIZE,
|
||||||
|
crc
|
||||||
|
);
|
||||||
|
|
||||||
|
let checksum = checksum(unsafe { &_moonboot_state_data_start });
|
||||||
|
if crc == checksum {
|
||||||
|
let data = MoonbootState::deserialize_from(unsafe { &_moonboot_state_data_start });
|
||||||
|
log::trace!("CRC Match! {}: {:?}", crc, data);
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
log::trace!("CRC Mismatch! {} vs {}", crc, checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
MoonbootState {
|
||||||
|
update: Update::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, data: &MoonbootState) -> Result<(), ()> {
|
||||||
|
log::trace!("Writing data {:?}", data);
|
||||||
|
|
||||||
|
unsafe { _moonboot_state_data_start = data.serialize() };
|
||||||
|
log::trace!("Written data: {:?}", unsafe { &_moonboot_state_data_start });
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
_moonboot_state_crc_start = checksum(&_moonboot_state_data_start);
|
||||||
|
}
|
||||||
|
log::info!(
|
||||||
|
"Written len: {}, checksum: {}",
|
||||||
|
STATE_SERIALIZED_MAX_SIZE,
|
||||||
|
unsafe { _moonboot_state_crc_start }
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Exchange for RamState {
|
||||||
fn exchange<InternalMemory: Storage, HardwareState: State, const INTERNAL_PAGE_SIZE: usize>(
|
fn exchange<InternalMemory: Storage, HardwareState: State, const INTERNAL_PAGE_SIZE: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
internal_memory: &mut InternalMemory,
|
internal_memory: &mut InternalMemory,
|
|
@ -1,25 +0,0 @@
|
||||||
pub mod ram;
|
|
||||||
pub mod scratch;
|
|
||||||
|
|
||||||
use embedded_storage::Storage;
|
|
||||||
|
|
||||||
use crate::state::{ExchangeProgress, State};
|
|
||||||
|
|
||||||
/// Error occured during memory access
|
|
||||||
#[cfg_attr(feature = "use-defmt", derive(Format))]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum MemoryError {
|
|
||||||
BankSizeNotEqual,
|
|
||||||
BankSizeZero,
|
|
||||||
ReadFailure,
|
|
||||||
WriteFailure,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Swap {
|
|
||||||
fn exchange<InternalMemory: Storage, HardwareState: State, const INTERNAL_PAGE_SIZE: usize>(
|
|
||||||
&mut self,
|
|
||||||
internal_memory: &mut InternalMemory,
|
|
||||||
state: &mut HardwareState,
|
|
||||||
exchange: ExchangeProgress,
|
|
||||||
) -> Result<(), MemoryError>;
|
|
||||||
}
|
|
Loading…
Reference in a new issue