mirror of
https://github.com/jhbruhn/moonboot.git
synced 2025-03-15 01:55:50 +00:00
Merge a301aaa9ea
into efadc79b9e
This commit is contained in:
commit
afb21b7a02
12 changed files with 536 additions and 356 deletions
29
Cargo.lock
generated
29
Cargo.lock
generated
|
@ -53,6 +53,12 @@ version = "0.13.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
|
@ -106,21 +112,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "defmt"
|
||||
version = "0.2.3"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15fe96f5d208164afa70583ff8f062e7697cbbb0b98e5076fbf8ac6da9edff0f"
|
||||
checksum = "d3a0ae7494d9bff013d7b89471f4c424356a71e9752e0c78abe7e6c608a16bb3"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"defmt-macros",
|
||||
"semver 1.0.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-macros"
|
||||
version = "0.2.3"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bd2c3949cb76c25f48c363e61b97f05b317efe3c12fa45d54a6599c3949c85e"
|
||||
checksum = "6d944432e281084511691b36e5e9c794c19c33675822c9019e3b64f5b89e10da"
|
||||
dependencies = [
|
||||
"defmt-parser",
|
||||
"proc-macro-error",
|
||||
"proc-macro2 1.0.37",
|
||||
"quote 1.0.18",
|
||||
"syn 1.0.91",
|
||||
|
@ -128,9 +135,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "defmt-parser"
|
||||
version = "0.2.2"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc621c2b4f5f5635e34021c38af2ccb0c1dae38ba11ebee25258de8bb1cee9fe"
|
||||
checksum = "0db23d29972d99baa3de2ee2ae3f104c10564a6d05a346eb3f4c4f2c0525a06e"
|
||||
|
||||
[[package]]
|
||||
name = "desse"
|
||||
|
@ -372,7 +379,7 @@ version = "0.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver 0.9.0",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -390,12 +397,6 @@ dependencies = [
|
|||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4"
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
|
|
|
@ -15,7 +15,7 @@ moonboot-macros = { path = "./macros", version = "0.1.2" }
|
|||
heapless = {version = "0.7", features = ["serde"] }
|
||||
serde = { version = "1.0", features = ["derive"], default-features = false, optional = true }
|
||||
cortex-m = { version = "0.7", optional = true }
|
||||
defmt = { version = "0.2", optional = true }
|
||||
defmt = { version = "0.3", optional = true }
|
||||
logger-crate = { version = "0.4", optional = true, package = "log" }
|
||||
crc = "2.0"
|
||||
desse = { version = "0.2.1", optional = true }
|
||||
|
@ -38,7 +38,7 @@ defmt-warn = []
|
|||
defmt-error = []
|
||||
|
||||
[package.metadata.release]
|
||||
enable-features = ["ram-state", "ram-state", "cortex-m"]
|
||||
enable-features = ["ram-state", "cortex-m"]
|
||||
shared-version = true
|
||||
dependent-version = "upgrade"
|
||||
pre-release-replacements = [
|
||||
|
|
194
src/boot/mod.rs
194
src/boot/mod.rs
|
@ -1,77 +1,57 @@
|
|||
use crate::{
|
||||
exchange::Exchange,
|
||||
hardware::processor::Processor,
|
||||
hardware::{Bank, Config},
|
||||
state::{ExchangeProgress, State, Update, UpdateError},
|
||||
Address,
|
||||
state::{ExchangeProgress, ExchangeStep, State, Update, UpdateError},
|
||||
Context,
|
||||
};
|
||||
|
||||
use embedded_storage::Storage;
|
||||
|
||||
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 {
|
||||
BankSizeNotEqual,
|
||||
BankSizeZero,
|
||||
ReadFailure,
|
||||
WriteFailure,
|
||||
}
|
||||
|
||||
/// 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 MoonbootBoot<
|
||||
InternalMemory: Storage,
|
||||
HardwareState: State,
|
||||
CPU: Processor, // TODO: Wrap these into a context struct like rubble?
|
||||
const INTERNAL_PAGE_SIZE: usize,
|
||||
> {
|
||||
pub struct MoonbootBoot<CONTEXT: Context, const INTERNAL_PAGE_SIZE: usize> {
|
||||
config: Config,
|
||||
internal_memory: InternalMemory,
|
||||
state: HardwareState,
|
||||
processor: CPU,
|
||||
storage: CONTEXT::Storage,
|
||||
state: CONTEXT::State,
|
||||
processor: CONTEXT::Processor,
|
||||
exchange: CONTEXT::Exchange,
|
||||
}
|
||||
|
||||
impl<
|
||||
InternalMemory: Storage,
|
||||
HardwareState: State,
|
||||
CPU: Processor,
|
||||
const INTERNAL_PAGE_SIZE: usize,
|
||||
> MoonbootBoot<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
|
||||
{
|
||||
impl<CONTEXT: Context, const INTERNAL_PAGE_SIZE: usize> MoonbootBoot<CONTEXT, INTERNAL_PAGE_SIZE> {
|
||||
/// create a new instance of the bootloader
|
||||
pub fn new(
|
||||
config: Config,
|
||||
internal_memory: InternalMemory,
|
||||
state: HardwareState,
|
||||
processor: CPU,
|
||||
) -> MoonbootBoot<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> {
|
||||
storage: CONTEXT::Storage,
|
||||
state: CONTEXT::State,
|
||||
processor: CONTEXT::Processor,
|
||||
exchange: CONTEXT::Exchange,
|
||||
) -> Self {
|
||||
Self {
|
||||
config,
|
||||
internal_memory,
|
||||
storage,
|
||||
state,
|
||||
processor,
|
||||
exchange,
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroy this instance of the bootloader and return access to the hardware peripheral
|
||||
pub fn destroy(self) -> (InternalMemory, HardwareState, CPU) {
|
||||
(self.internal_memory, self.state, self.processor)
|
||||
pub fn destroy(self) -> (CONTEXT::Storage, CONTEXT::State, CONTEXT::Processor) {
|
||||
(self.storage, self.state, self.processor)
|
||||
}
|
||||
|
||||
/// Execute the update and boot logic of the bootloader
|
||||
pub fn boot(&mut self) -> Result<void::Void, ()> {
|
||||
pub fn boot(&mut self) -> Result<void::Void, <CONTEXT::State as State>::Error> {
|
||||
// TODO: consider error handling
|
||||
log::info!("Booting with moonboot!");
|
||||
|
||||
self.processor.setup(&self.config);
|
||||
|
||||
let mut state = self.state.read();
|
||||
let mut state = self.state.read()?;
|
||||
|
||||
log::info!("Old State: {:?}", state);
|
||||
|
||||
|
@ -80,16 +60,14 @@ impl<
|
|||
Update::None => self.handle_none(),
|
||||
Update::Request(bank) => self.handle_request(bank),
|
||||
Update::Revert(bank) => self.handle_revert(bank),
|
||||
Update::Exchanging(progress) => self.handle_exchanging(progress),
|
||||
Update::Exchanging(progress) => self.handle_exchanging(progress)?,
|
||||
Update::Error(err) => Update::Error(err),
|
||||
};
|
||||
|
||||
// TODO: Handle Progress Variable in state to recover from power loss
|
||||
|
||||
log::info!("New State: {:?}", state);
|
||||
|
||||
// Step 2: Update state of Bootloader
|
||||
self.state.write(state)?;
|
||||
self.state.write(&state)?;
|
||||
|
||||
// Step 3: Jump to new or unchanged firmware
|
||||
self.jump_to_firmware();
|
||||
|
@ -125,17 +103,24 @@ impl<
|
|||
|
||||
// Handle a case of power interruption or similar, which lead to a exchange_banks being
|
||||
// interrupted.
|
||||
fn handle_exchanging(&mut self, progress: ExchangeProgress) -> Update {
|
||||
fn handle_exchanging(
|
||||
&mut self,
|
||||
progress: ExchangeProgress,
|
||||
) -> Result<Update, <CONTEXT::State as State>::Error> {
|
||||
log::error!(
|
||||
"Firmware Update was interrupted! Trying to recover with exchange operation: {:?}",
|
||||
progress
|
||||
);
|
||||
|
||||
let exchange_result =
|
||||
self.exchange_banks_with_start(progress.a, progress.b, progress.page_index);
|
||||
let exchange_result = self.exchange.exchange::<INTERNAL_PAGE_SIZE>(
|
||||
&self.config,
|
||||
&mut self.storage,
|
||||
&mut self.state,
|
||||
progress,
|
||||
);
|
||||
|
||||
if exchange_result.is_ok() {
|
||||
let state = self.state.read().update;
|
||||
Ok(if exchange_result.is_ok() {
|
||||
let state = self.state.read()?.update;
|
||||
match state {
|
||||
Update::Exchanging(progress) => {
|
||||
if progress.recovering {
|
||||
|
@ -152,7 +137,7 @@ impl<
|
|||
exchange_result
|
||||
);
|
||||
Update::Error(UpdateError::ImageExchangeFailed)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Revert the bootable image with the image in index new_firmware. Returns Revert on success if
|
||||
|
@ -172,7 +157,18 @@ impl<
|
|||
);
|
||||
|
||||
// Try to exchange the firmware images
|
||||
let exchange_result = self.exchange_banks(new, old);
|
||||
let exchange_result = self.exchange.exchange::<INTERNAL_PAGE_SIZE>(
|
||||
&self.config,
|
||||
&mut self.storage,
|
||||
&mut self.state,
|
||||
ExchangeProgress {
|
||||
a: new,
|
||||
b: old,
|
||||
page_index: 0,
|
||||
recovering: false,
|
||||
step: ExchangeStep::AToScratch,
|
||||
},
|
||||
);
|
||||
if exchange_result.is_ok() {
|
||||
if with_failsafe_revert {
|
||||
// Update Firmware Update State to revert. The Application will set this to
|
||||
|
@ -197,102 +193,6 @@ impl<
|
|||
}
|
||||
}
|
||||
|
||||
fn exchange_banks(&mut self, a: Bank, b: Bank) -> Result<(), MemoryError> {
|
||||
self.exchange_banks_with_start(a, b, 0)
|
||||
}
|
||||
|
||||
fn exchange_banks_with_start(
|
||||
&mut self,
|
||||
a: Bank,
|
||||
b: Bank,
|
||||
start_index: u32,
|
||||
) -> Result<(), MemoryError> {
|
||||
// TODO: Sanity Check start_index
|
||||
if a.size != b.size {
|
||||
return Err(MemoryError::BankSizeNotEqual);
|
||||
}
|
||||
|
||||
if a.size == 0 || b.size == 0 {
|
||||
return Err(MemoryError::BankSizeZero);
|
||||
}
|
||||
|
||||
let size = a.size; // Both are equal
|
||||
|
||||
let full_pages = size / INTERNAL_PAGE_SIZE as Address;
|
||||
let remaining_page_length = size as usize % INTERNAL_PAGE_SIZE;
|
||||
|
||||
let mut page_a_buf = [0_u8; INTERNAL_PAGE_SIZE];
|
||||
let mut page_b_buf = [0_u8; INTERNAL_PAGE_SIZE];
|
||||
// can we reduce this to 1 buf and fancy operations?
|
||||
// probably not with the read/write API.
|
||||
// classic memory exchange problem :)
|
||||
|
||||
// Set this in the exchanging part to know whether we are in a recovery process from a
|
||||
// failed update or on the initial update
|
||||
let recovering = matches!(self.state.read().update, Update::Revert(_));
|
||||
|
||||
// TODO: Fix
|
||||
let a_location = a.location;
|
||||
let b_location = b.location;
|
||||
|
||||
for page_index in start_index..full_pages {
|
||||
let offset = page_index * INTERNAL_PAGE_SIZE as u32;
|
||||
log::trace!(
|
||||
"Exchange: Page {}, from a ({}) to b ({})",
|
||||
page_index,
|
||||
a_location + offset,
|
||||
b_location + offset
|
||||
);
|
||||
self.internal_memory
|
||||
.read(a_location + offset, &mut page_a_buf)
|
||||
.map_err(|_| MemoryError::ReadFailure)?;
|
||||
self.internal_memory
|
||||
.read(b_location + offset, &mut page_b_buf)
|
||||
.map_err(|_| MemoryError::ReadFailure)?;
|
||||
self.internal_memory
|
||||
.write(a_location + offset, &page_b_buf)
|
||||
.map_err(|_| MemoryError::WriteFailure)?;
|
||||
self.internal_memory
|
||||
.write(b_location + offset, &page_a_buf)
|
||||
.map_err(|_| MemoryError::WriteFailure)?;
|
||||
|
||||
// Store the exchange progress
|
||||
let mut state = self.state.read();
|
||||
state.update = Update::Exchanging(ExchangeProgress {
|
||||
a,
|
||||
b,
|
||||
recovering,
|
||||
page_index,
|
||||
});
|
||||
// TODO: Ignore the error here?
|
||||
let _ = self.state.write(state);
|
||||
}
|
||||
// TODO: Fit this into the while loop
|
||||
if remaining_page_length > 0 {
|
||||
let offset = full_pages * INTERNAL_PAGE_SIZE as u32;
|
||||
|
||||
self.internal_memory
|
||||
.read(
|
||||
a.location + offset,
|
||||
&mut page_a_buf[0..remaining_page_length],
|
||||
)
|
||||
.map_err(|_| MemoryError::ReadFailure)?;
|
||||
self.internal_memory
|
||||
.read(
|
||||
b.location + offset,
|
||||
&mut page_b_buf[0..remaining_page_length],
|
||||
)
|
||||
.map_err(|_| MemoryError::ReadFailure)?;
|
||||
self.internal_memory
|
||||
.write(a.location + offset, &page_a_buf[0..remaining_page_length])
|
||||
.map_err(|_| MemoryError::WriteFailure)?;
|
||||
self.internal_memory
|
||||
.write(b.location + offset, &page_b_buf[0..remaining_page_length])
|
||||
.map_err(|_| MemoryError::WriteFailure)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Jump to the firmware image marked as bootable
|
||||
fn jump_to_firmware(&mut self) -> ! {
|
||||
let app_exec_image = self.config.boot_bank;
|
||||
|
|
22
src/exchange/mod.rs
Normal file
22
src/exchange/mod.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
pub mod ram;
|
||||
|
||||
pub mod scratch;
|
||||
|
||||
use crate::{
|
||||
hardware::Config,
|
||||
state::{ExchangeProgress, State},
|
||||
};
|
||||
use embedded_storage::Storage;
|
||||
|
||||
/// Abstraction for the exchange operation of the current state.
|
||||
pub trait Exchange<STORAGE: Storage, STATE: State> {
|
||||
type Error: core::fmt::Debug;
|
||||
|
||||
fn exchange<const INTERNAL_PAGE_SIZE: usize>(
|
||||
&mut self,
|
||||
config: &Config,
|
||||
storage: &mut STORAGE,
|
||||
state: &mut STATE,
|
||||
progress: ExchangeProgress,
|
||||
) -> Result<(), Self::Error>;
|
||||
}
|
129
src/exchange/ram.rs
Normal file
129
src/exchange/ram.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
use embedded_storage::Storage;
|
||||
|
||||
use crate::{
|
||||
exchange::Exchange,
|
||||
hardware::Config,
|
||||
log,
|
||||
state::{ExchangeProgress, State, Update},
|
||||
Address,
|
||||
};
|
||||
|
||||
pub struct Ram;
|
||||
|
||||
pub enum ExchangeError<STORAGE, STATE> {
|
||||
Storage(STORAGE),
|
||||
State(STATE),
|
||||
}
|
||||
|
||||
impl<STORAGE, STATE> core::fmt::Debug for ExchangeError<STORAGE, STATE>
|
||||
where
|
||||
STORAGE: core::fmt::Debug,
|
||||
STATE: core::fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Self::Storage(arg0) => f.debug_tuple("Storage").field(arg0).finish(),
|
||||
Self::State(arg0) => f.debug_tuple("State").field(arg0).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<STORAGE: Storage, STATE: State> Exchange<STORAGE, STATE> for Ram
|
||||
where
|
||||
STORAGE::Error: core::fmt::Debug,
|
||||
STATE::Error: core::fmt::Debug,
|
||||
{
|
||||
type Error = ExchangeError<STORAGE::Error, STATE::Error>;
|
||||
|
||||
fn exchange<const INTERNAL_PAGE_SIZE: usize>(
|
||||
&mut self,
|
||||
_config: &Config,
|
||||
storage: &mut STORAGE,
|
||||
state: &mut STATE,
|
||||
progress: ExchangeProgress,
|
||||
) -> Result<(), Self::Error> {
|
||||
let ExchangeProgress {
|
||||
a,
|
||||
b,
|
||||
page_index,
|
||||
step,
|
||||
..
|
||||
} = progress;
|
||||
|
||||
assert_eq!(a.size, b.size);
|
||||
assert_ne!(a.size, 0);
|
||||
assert_ne!(b.size, 0);
|
||||
|
||||
let size = a.size; // Both are equal
|
||||
|
||||
let full_pages = size / INTERNAL_PAGE_SIZE as Address;
|
||||
let remaining_page_length = size as usize % INTERNAL_PAGE_SIZE;
|
||||
|
||||
let mut page_a_buf = [0_u8; INTERNAL_PAGE_SIZE];
|
||||
let mut page_b_buf = [0_u8; INTERNAL_PAGE_SIZE];
|
||||
|
||||
let mut last_state = state.read().map_err(ExchangeError::State)?;
|
||||
|
||||
// Set this in the exchanging part to know whether we are in a recovery process from a
|
||||
// failed update or on the initial update
|
||||
let recovering = matches!(last_state.update, Update::Revert(_));
|
||||
|
||||
for page_index in page_index..full_pages {
|
||||
let offset = page_index * INTERNAL_PAGE_SIZE as u32;
|
||||
let a_location = a.location + offset;
|
||||
let b_location = b.location + offset;
|
||||
log::trace!(
|
||||
"Exchange: Page {}, from a ({}) to b ({})",
|
||||
page_index,
|
||||
a_location,
|
||||
b_location
|
||||
);
|
||||
|
||||
storage
|
||||
.read(a_location, &mut page_a_buf)
|
||||
.map_err(ExchangeError::Storage)?;
|
||||
storage
|
||||
.read(b_location, &mut page_b_buf)
|
||||
.map_err(ExchangeError::Storage)?;
|
||||
storage
|
||||
.write(a_location, &page_b_buf)
|
||||
.map_err(ExchangeError::Storage)?;
|
||||
storage
|
||||
.write(b_location, &page_a_buf)
|
||||
.map_err(ExchangeError::Storage)?;
|
||||
|
||||
// Store the exchange progress
|
||||
|
||||
last_state.update = Update::Exchanging(ExchangeProgress {
|
||||
a,
|
||||
b,
|
||||
recovering,
|
||||
page_index,
|
||||
step,
|
||||
});
|
||||
|
||||
state.write(&last_state).map_err(ExchangeError::State)?;
|
||||
}
|
||||
// TODO: Fit this into the while loop
|
||||
if remaining_page_length > 0 {
|
||||
let offset = full_pages * INTERNAL_PAGE_SIZE as u32;
|
||||
let a_location = a.location + offset;
|
||||
let b_location = b.location + offset;
|
||||
|
||||
storage
|
||||
.read(a_location, &mut page_a_buf[0..remaining_page_length])
|
||||
.map_err(ExchangeError::Storage)?;
|
||||
storage
|
||||
.read(b_location, &mut page_b_buf[0..remaining_page_length])
|
||||
.map_err(ExchangeError::Storage)?;
|
||||
storage
|
||||
.write(a_location, &page_a_buf[0..remaining_page_length])
|
||||
.map_err(ExchangeError::Storage)?;
|
||||
storage
|
||||
.write(b_location + offset, &page_b_buf[0..remaining_page_length])
|
||||
.map_err(ExchangeError::Storage)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
154
src/exchange/scratch.rs
Normal file
154
src/exchange/scratch.rs
Normal file
|
@ -0,0 +1,154 @@
|
|||
use core::fmt::Debug;
|
||||
|
||||
use embedded_storage::Storage;
|
||||
|
||||
use crate::{
|
||||
exchange::Exchange,
|
||||
hardware::Config,
|
||||
log,
|
||||
state::{ExchangeProgress, ExchangeStep, State, Update},
|
||||
Address,
|
||||
};
|
||||
|
||||
pub struct Scratch;
|
||||
|
||||
pub enum ExchangeError<STORAGE, STATE> {
|
||||
Storage(STORAGE),
|
||||
State(STATE),
|
||||
ScratchInsufficient,
|
||||
}
|
||||
|
||||
impl<STORAGE, STATE> Debug for ExchangeError<STORAGE, STATE>
|
||||
where
|
||||
STORAGE: Debug,
|
||||
STATE: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Self::Storage(arg0) => f.debug_tuple("Storage").field(arg0).finish(),
|
||||
Self::State(arg0) => f.debug_tuple("State").field(arg0).finish(),
|
||||
Self::ScratchInsufficient => f.write_str("ScratchInsufficient"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<STORAGE: Storage, STATE: State> Exchange<STORAGE, STATE> for Scratch
|
||||
where
|
||||
STORAGE::Error: Debug,
|
||||
STATE::Error: Debug,
|
||||
{
|
||||
type Error = ExchangeError<STORAGE::Error, STATE::Error>;
|
||||
|
||||
fn exchange<const INTERNAL_PAGE_SIZE: usize>(
|
||||
&mut self,
|
||||
config: &Config,
|
||||
storage: &mut STORAGE,
|
||||
state: &mut STATE,
|
||||
progress: ExchangeProgress,
|
||||
) -> Result<(), Self::Error> {
|
||||
let ExchangeProgress {
|
||||
a,
|
||||
b,
|
||||
page_index,
|
||||
mut step,
|
||||
..
|
||||
} = progress;
|
||||
|
||||
assert_eq!(a.size, b.size);
|
||||
assert_ne!(a.size, 0);
|
||||
assert_ne!(b.size, 0);
|
||||
|
||||
if config.scratch_bank.size < INTERNAL_PAGE_SIZE as u32 {
|
||||
return Err(ExchangeError::ScratchInsufficient);
|
||||
}
|
||||
|
||||
let size = a.size; // Both are equal
|
||||
|
||||
let full_pages = size / INTERNAL_PAGE_SIZE as Address;
|
||||
let remaining_page_length = size as usize % INTERNAL_PAGE_SIZE;
|
||||
|
||||
assert_eq!(remaining_page_length, 0);
|
||||
|
||||
let mut ram_buf = [0_u8; INTERNAL_PAGE_SIZE];
|
||||
|
||||
let mut last_state = state.read().map_err(ExchangeError::State)?;
|
||||
|
||||
// Set this in the exchanging part to know whether we are in a recovery process from a
|
||||
// failed update or on the initial update
|
||||
let recovering = matches!(last_state.update, Update::Revert(_));
|
||||
|
||||
let a_location = a.location;
|
||||
let b_location = b.location;
|
||||
|
||||
let scratch_bank_pages = config.scratch_bank.size / INTERNAL_PAGE_SIZE as u32;
|
||||
|
||||
let mut first = true;
|
||||
for page_index in page_index..full_pages {
|
||||
let offset = page_index * INTERNAL_PAGE_SIZE as u32;
|
||||
|
||||
let a_location = a_location + offset;
|
||||
let b_location = b_location + offset;
|
||||
|
||||
let scratch_index = page_index % scratch_bank_pages;
|
||||
let scratch_offset = scratch_index * INTERNAL_PAGE_SIZE as u32;
|
||||
let scratch_location = config.scratch_bank.location + scratch_offset;
|
||||
|
||||
log::trace!(
|
||||
"Exchange: Page {}, from a ({}) to b ({}) using scratch ({})",
|
||||
page_index,
|
||||
a_location,
|
||||
b_location,
|
||||
scratch_location
|
||||
);
|
||||
|
||||
loop {
|
||||
if first {
|
||||
// Do not write the state to flash, as it is already recent.
|
||||
first = false;
|
||||
} else {
|
||||
last_state.update = Update::Exchanging(ExchangeProgress {
|
||||
a,
|
||||
b,
|
||||
recovering,
|
||||
page_index,
|
||||
step,
|
||||
});
|
||||
state.write(&last_state).map_err(ExchangeError::State)?;
|
||||
}
|
||||
|
||||
match step {
|
||||
ExchangeStep::AToScratch => {
|
||||
storage
|
||||
.read(a_location, &mut ram_buf)
|
||||
.map_err(ExchangeError::Storage)?;
|
||||
storage
|
||||
.write(scratch_location, &ram_buf)
|
||||
.map_err(ExchangeError::Storage)?;
|
||||
step = ExchangeStep::BToA;
|
||||
}
|
||||
ExchangeStep::BToA => {
|
||||
storage
|
||||
.read(b_location, &mut ram_buf)
|
||||
.map_err(ExchangeError::Storage)?;
|
||||
storage
|
||||
.write(a_location, &ram_buf)
|
||||
.map_err(ExchangeError::Storage)?;
|
||||
step = ExchangeStep::ScratchToB;
|
||||
}
|
||||
ExchangeStep::ScratchToB => {
|
||||
storage
|
||||
.read(scratch_location, &mut ram_buf)
|
||||
.map_err(ExchangeError::Storage)?;
|
||||
storage
|
||||
.write(b_location, &ram_buf)
|
||||
.map_err(ExchangeError::Storage)?;
|
||||
step = ExchangeStep::AToScratch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -47,6 +47,8 @@ pub struct Config {
|
|||
pub update_bank: Bank,
|
||||
/// bank the bootloader is contained in, switching between banks
|
||||
pub bootloader_bank: Bank,
|
||||
/// bank the pages are temporarily stored when using the `Scratch` exchange method
|
||||
pub scratch_bank: Bank,
|
||||
// Initial Image is stored to this bank after first update, restore on failure
|
||||
// pub golden_bank: Bank,
|
||||
/// section of RAM of this device
|
||||
|
|
|
@ -29,15 +29,14 @@ pub mod cortex_m {
|
|||
fn do_jump(&mut self, address: super::Address) -> ! {
|
||||
unsafe {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(&mut self, config: &crate::hardware::Config) {
|
||||
fn setup(&mut self, _config: &crate::hardware::Config) {
|
||||
// Nothing to do!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -11,13 +11,17 @@
|
|||
//!* Automatic Linker Script generation based on a Section/Parition Description in Rust Code
|
||||
|
||||
mod boot;
|
||||
|
||||
/// Implementations for use in the bootloader
|
||||
pub use boot::MoonbootBoot;
|
||||
|
||||
mod manager;
|
||||
|
||||
/// Implementations for use in the firmware
|
||||
pub use manager::MoonbootManager;
|
||||
|
||||
/// Various processes for exchanging pages
|
||||
pub mod exchange;
|
||||
/// Common hardware abstractions and associated implementations
|
||||
pub mod hardware;
|
||||
/// Shared state management between firmware and bootloader
|
||||
|
@ -43,6 +47,13 @@ pub(crate) use defmt as log;
|
|||
#[cfg(feature = "use-log")]
|
||||
pub(crate) use logger_crate as log;
|
||||
|
||||
pub trait Context {
|
||||
type Storage: embedded_storage::Storage;
|
||||
type State: state::State;
|
||||
type Processor: hardware::processor::Processor;
|
||||
type Exchange: exchange::Exchange<Self::Storage, Self::State>;
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "use-log", feature = "use-defmt")))]
|
||||
pub(crate) mod log {
|
||||
macro_rules! info {
|
||||
|
|
|
@ -1,57 +1,74 @@
|
|||
use crate::{
|
||||
hardware::{processor::Processor, Config},
|
||||
state::{State, Update},
|
||||
Context,
|
||||
};
|
||||
|
||||
use embedded_storage::{ReadStorage, Storage};
|
||||
|
||||
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 MoonbootManager<
|
||||
InternalMemory: Storage,
|
||||
HardwareState: State,
|
||||
CPU: Processor,
|
||||
const INTERNAL_PAGE_SIZE: usize,
|
||||
> {
|
||||
pub struct MoonbootManager<CONTEXT: Context, const INTERNAL_PAGE_SIZE: usize> {
|
||||
config: Config,
|
||||
internal_memory: InternalMemory,
|
||||
state: HardwareState,
|
||||
processor: CPU,
|
||||
storage: CONTEXT::Storage,
|
||||
state: CONTEXT::State,
|
||||
processor: CONTEXT::Processor,
|
||||
}
|
||||
|
||||
impl<
|
||||
InternalMemory: Storage,
|
||||
HardwareState: State,
|
||||
CPU: Processor,
|
||||
const INTERNAL_PAGE_SIZE: usize,
|
||||
> MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
|
||||
#[cfg_attr(feature = "use-defmt", derive(Format))]
|
||||
#[derive(Debug)]
|
||||
pub struct InitError;
|
||||
|
||||
#[cfg_attr(feature = "use-defmt", derive(Format))]
|
||||
#[derive(Debug)]
|
||||
pub enum MarkError<E: core::fmt::Debug> {
|
||||
UpdateQueuedButNotInstalled,
|
||||
State(E),
|
||||
}
|
||||
|
||||
impl<CONTEXT: Context, const INTERNAL_PAGE_SIZE: usize>
|
||||
MoonbootManager<CONTEXT, INTERNAL_PAGE_SIZE>
|
||||
{
|
||||
pub fn new(
|
||||
config: Config,
|
||||
internal_memory: InternalMemory,
|
||||
state: HardwareState,
|
||||
processor: CPU,
|
||||
) -> MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> {
|
||||
Self {
|
||||
storage: CONTEXT::Storage,
|
||||
state: CONTEXT::State,
|
||||
processor: CONTEXT::Processor,
|
||||
) -> Result<Self, InitError> {
|
||||
if config.update_bank.size > config.boot_bank.size {
|
||||
log::error!(
|
||||
"Requested update bank {:?} is larger than boot bank {:?}",
|
||||
config.update_bank,
|
||||
config.boot_bank
|
||||
);
|
||||
return Err(InitError);
|
||||
}
|
||||
|
||||
if config.update_bank.size == 0 || config.boot_bank.size == 0 {
|
||||
log::error!("Requested banks are of zero size");
|
||||
return Err(InitError);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
config,
|
||||
internal_memory,
|
||||
storage,
|
||||
state,
|
||||
processor,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Destroy this instance of the boot manager and return access to the hardware peripheral
|
||||
pub fn destroy(self) -> (InternalMemory, HardwareState, CPU) {
|
||||
(self.internal_memory, self.state, self.processor)
|
||||
pub fn destroy(self) -> (CONTEXT::Storage, CONTEXT::State, CONTEXT::Processor) {
|
||||
(self.storage, self.state, self.processor)
|
||||
}
|
||||
|
||||
/// Run this immediately after booting your new image successfully to mark the boot as
|
||||
/// succesful. If you do not do this, any reset will cause the bootloader to restore to the
|
||||
/// previous firmware image.
|
||||
pub fn mark_boot_successful(&mut self) -> Result<(), ()> {
|
||||
let mut current_state = self.state.read();
|
||||
pub fn mark_boot_successful(
|
||||
&mut self,
|
||||
) -> Result<(), MarkError<<CONTEXT::State as State>::Error>> {
|
||||
let mut current_state = self.state.read().map_err(MarkError::State)?;
|
||||
|
||||
log::info!(
|
||||
"Application running, marking boot as successful. Current state: {:?}",
|
||||
|
@ -69,34 +86,24 @@ impl<
|
|||
}
|
||||
_ => {
|
||||
log::error!("There is an update queued, but it has not been installed yet. Did you skip the bootloader?");
|
||||
return Err(());
|
||||
return Err(MarkError::UpdateQueuedButNotInstalled);
|
||||
}
|
||||
};
|
||||
|
||||
log::trace!("New state: {:?}", current_state);
|
||||
|
||||
self.state.write(current_state)
|
||||
self.state.write(¤t_state).map_err(MarkError::State)
|
||||
}
|
||||
|
||||
// Upgrade firmware verifiying the given signature over the size of size.
|
||||
// Can only return an error or diverge (!, represented by Void while ! is not a type yet)
|
||||
pub fn update(&mut self) -> Result<void::Void, ()> {
|
||||
pub fn update(&mut self) -> Result<void::Void, <CONTEXT::State as State>::Error> {
|
||||
// Apply the update stored in the update bank
|
||||
let bank = self.config.update_bank;
|
||||
// TODO: Check size value!
|
||||
|
||||
log::info!("Update requested on slot {:?}", bank);
|
||||
|
||||
if bank.size > self.config.boot_bank.size {
|
||||
log::error!(
|
||||
"Requested update bank {:?} is larger than boot bank {:?}",
|
||||
bank,
|
||||
self.config.boot_bank
|
||||
);
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let mut current_state = self.state.read();
|
||||
let mut current_state = self.state.read()?;
|
||||
|
||||
if current_state.update != Update::None {
|
||||
log::warn!(
|
||||
|
@ -107,7 +114,7 @@ impl<
|
|||
|
||||
current_state.update = Update::Request(bank);
|
||||
|
||||
self.state.write(current_state)?;
|
||||
self.state.write(¤t_state)?;
|
||||
|
||||
log::info!("Stored update request, jumping to bootloader! Geronimo!");
|
||||
|
||||
|
@ -124,79 +131,3 @@ impl<
|
|||
self.processor.do_jump(bootloader_address)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
|
||||
{
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
self.config.update_bank.location as *const u8,
|
||||
self.config.update_bank.size as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read Access to the current update target slot
|
||||
impl<
|
||||
InternalMemory: Storage,
|
||||
HardwareState: State,
|
||||
CPU: Processor,
|
||||
const INTERNAL_PAGE_SIZE: usize,
|
||||
> ReadStorage for MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
|
||||
{
|
||||
type Error = (); // TODO
|
||||
|
||||
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
|
||||
let bank = self.config.update_bank; // For now we always write updates to this bank.
|
||||
if offset > bank.size || offset + bytes.len() as u32 > bank.size {
|
||||
Err(()) // TODO: We want better error types!
|
||||
} else {
|
||||
// TODO! fix
|
||||
let bank_start = bank.location;
|
||||
log::info!("Writing at {:x}[{:x}]", bank_start, offset);
|
||||
match bank.memory_unit {
|
||||
crate::hardware::MemoryUnit::Internal => {
|
||||
{ self.internal_memory.read(bank_start + offset, bytes) }.map_err(|_| ())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn capacity(&self) -> usize {
|
||||
self.config.update_bank.size as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// Write Access to the current update target slot
|
||||
impl<
|
||||
InternalMemory: Storage,
|
||||
HardwareState: State,
|
||||
CPU: Processor,
|
||||
const INTERNAL_PAGE_SIZE: usize,
|
||||
> 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.
|
||||
if offset > bank.size || offset + bytes.len() as u32 > bank.size {
|
||||
Err(()) // TODO: We want better error types!
|
||||
} else {
|
||||
// TODO! fix
|
||||
let bank_start = bank.location;
|
||||
log::info!("Writing at {:x}[{:x}]", bank_start, offset);
|
||||
match bank.memory_unit {
|
||||
crate::hardware::MemoryUnit::Internal => {
|
||||
{ self.internal_memory.write(bank_start + offset, bytes) }.map_err(|_| ())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
#[cfg(feature = "ram-state")]
|
||||
pub mod ram;
|
||||
|
||||
use crate::hardware::Bank;
|
||||
use crate::log;
|
||||
|
||||
use core::fmt::Debug;
|
||||
|
||||
use crc::{Crc, CRC_32_CKSUM};
|
||||
#[cfg(feature = "defmt")]
|
||||
use defmt::Format;
|
||||
#[cfg(feature = "ram-state")]
|
||||
#[cfg(any(feature = "ram-state", feature = "scratch-state"))]
|
||||
use desse::{Desse, DesseSized};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -14,7 +18,10 @@ use serde::{Deserialize, Serialize};
|
|||
// exchanged via software updates easily
|
||||
#[cfg_attr(feature = "use-defmt", derive(Format))]
|
||||
#[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)]
|
||||
pub enum Update {
|
||||
// No update requested, just jump to the application
|
||||
|
@ -33,7 +40,10 @@ pub enum Update {
|
|||
|
||||
#[cfg_attr(feature = "use-defmt", derive(Format))]
|
||||
#[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)]
|
||||
/// Errors that can occur during update
|
||||
pub enum UpdateError {
|
||||
|
@ -47,10 +57,30 @@ pub enum UpdateError {
|
|||
InvalidSignature,
|
||||
}
|
||||
|
||||
/// Upcoming operation of the exchange process for a given page index.
|
||||
#[cfg_attr(feature = "use-defmt", derive(Format))]
|
||||
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(
|
||||
any(feature = "ram-state", feature = "scratch-state"),
|
||||
derive(Desse, DesseSized)
|
||||
)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ExchangeStep {
|
||||
/// Copy page A to the scratch page.
|
||||
AToScratch,
|
||||
/// Copy page B to page A.
|
||||
BToA,
|
||||
/// Copy the scratch page to page B.
|
||||
ScratchToB,
|
||||
}
|
||||
|
||||
/// 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))]
|
||||
#[cfg_attr(
|
||||
any(feature = "ram-state", feature = "scratch-state"),
|
||||
derive(Desse, DesseSized)
|
||||
)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ExchangeProgress {
|
||||
/// Bank the update is coming from
|
||||
|
@ -59,6 +89,8 @@ pub struct ExchangeProgress {
|
|||
pub(crate) b: Bank,
|
||||
/// Page the operation has last copied
|
||||
pub(crate) page_index: u32,
|
||||
/// Upcoming operation of the exchange process for a given page index.
|
||||
pub(crate) step: ExchangeStep,
|
||||
/// Whether this exchange resulted from a Request (false) or a Revert (true)
|
||||
pub(crate) recovering: bool,
|
||||
}
|
||||
|
@ -66,7 +98,10 @@ pub struct ExchangeProgress {
|
|||
/// 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))]
|
||||
#[cfg_attr(
|
||||
any(feature = "ram-state", feature = "scratch-state"),
|
||||
derive(Desse, DesseSized)
|
||||
)]
|
||||
#[derive(Debug)]
|
||||
pub struct MoonbootState {
|
||||
/// If set Request, an Update is requested. This will exchange the two images, set the update
|
||||
|
@ -81,10 +116,12 @@ pub struct MoonbootState {
|
|||
/// 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 {
|
||||
type Error: Debug;
|
||||
|
||||
/// Read the shared state
|
||||
fn read(&mut self) -> MoonbootState;
|
||||
fn read(&mut self) -> Result<MoonbootState, Self::Error>;
|
||||
/// Write the new state to the shared state
|
||||
fn write(&mut self, data: MoonbootState) -> Result<(), ()>;
|
||||
fn write(&mut self, data: &MoonbootState) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Size of the serialized state
|
||||
|
@ -96,66 +133,3 @@ const CRC: Crc<StateCrcType> = Crc::<StateCrcType>::new(&CRC_32_CKSUM);
|
|||
fn checksum(bytes: &[u8]) -> StateCrcType {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
}
|
57
src/state/ram.rs
Normal file
57
src/state/ram.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use crate::log;
|
||||
|
||||
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 {
|
||||
type Error = void::Void;
|
||||
|
||||
fn read(&mut self) -> Result<MoonbootState, void::Void> {
|
||||
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);
|
||||
Ok(data)
|
||||
} else {
|
||||
log::trace!("CRC Mismatch! {} vs {}", crc, checksum);
|
||||
Ok(MoonbootState {
|
||||
update: Update::None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, data: &MoonbootState) -> Result<(), Self::Error> {
|
||||
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(())
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue