mirror of
https://github.com/jhbruhn/moonboot.git
synced 2025-03-15 01:55:50 +00:00
Added two differing swap modes for pages
This commit is contained in:
parent
efadc79b9e
commit
01fe251f1f
7 changed files with 328 additions and 123 deletions
144
src/boot/mod.rs
144
src/boot/mod.rs
|
@ -1,8 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
hardware::processor::Processor,
|
hardware::processor::Processor,
|
||||||
hardware::{Bank, Config},
|
hardware::{Bank, Config},
|
||||||
state::{ExchangeProgress, State, Update, UpdateError},
|
state::{ExchangeProgress, ExchangeStep, State, Update, UpdateError},
|
||||||
Address,
|
swap::{MemoryError, Swap},
|
||||||
};
|
};
|
||||||
|
|
||||||
use embedded_storage::Storage;
|
use embedded_storage::Storage;
|
||||||
|
@ -12,37 +12,29 @@ use crate::log;
|
||||||
#[cfg(feature = "defmt")]
|
#[cfg(feature = "defmt")]
|
||||||
use defmt::Format;
|
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
|
/// 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,
|
InternalMemory: Storage,
|
||||||
HardwareState: State,
|
HardwareState: State,
|
||||||
CPU: Processor, // TODO: Wrap these into a context struct like rubble?
|
CPU: Processor, // TODO: Wrap these into a context struct like rubble?
|
||||||
|
PageSwap: Swap,
|
||||||
const INTERNAL_PAGE_SIZE: usize,
|
const INTERNAL_PAGE_SIZE: usize,
|
||||||
> {
|
> {
|
||||||
config: Config,
|
config: Config,
|
||||||
internal_memory: InternalMemory,
|
internal_memory: InternalMemory,
|
||||||
state: HardwareState,
|
state: HardwareState,
|
||||||
processor: CPU,
|
processor: CPU,
|
||||||
|
swap: PageSwap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
InternalMemory: Storage,
|
InternalMemory: Storage,
|
||||||
HardwareState: State,
|
HardwareState: State,
|
||||||
CPU: Processor,
|
CPU: Processor,
|
||||||
|
PageSwap: Swap,
|
||||||
const INTERNAL_PAGE_SIZE: usize,
|
const INTERNAL_PAGE_SIZE: usize,
|
||||||
> MoonbootBoot<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
|
> MoonbootBoot<InternalMemory, HardwareState, CPU, PageSwap, INTERNAL_PAGE_SIZE>
|
||||||
{
|
{
|
||||||
/// create a new instance of the bootloader
|
/// create a new instance of the bootloader
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
@ -50,12 +42,14 @@ impl<
|
||||||
internal_memory: InternalMemory,
|
internal_memory: InternalMemory,
|
||||||
state: HardwareState,
|
state: HardwareState,
|
||||||
processor: CPU,
|
processor: CPU,
|
||||||
) -> MoonbootBoot<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> {
|
swap: PageSwap,
|
||||||
|
) -> MoonbootBoot<InternalMemory, HardwareState, CPU, PageSwap, INTERNAL_PAGE_SIZE> {
|
||||||
Self {
|
Self {
|
||||||
config,
|
config,
|
||||||
internal_memory,
|
internal_memory,
|
||||||
state,
|
state,
|
||||||
processor,
|
processor,
|
||||||
|
swap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,12 +78,10 @@ impl<
|
||||||
Update::Error(err) => Update::Error(err),
|
Update::Error(err) => Update::Error(err),
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Handle Progress Variable in state to recover from power loss
|
|
||||||
|
|
||||||
log::info!("New State: {:?}", state);
|
log::info!("New State: {:?}", state);
|
||||||
|
|
||||||
// Step 2: Update state of Bootloader
|
// Step 2: Update state of Bootloader
|
||||||
self.state.write(state)?;
|
self.state.write(&state)?;
|
||||||
|
|
||||||
// Step 3: Jump to new or unchanged firmware
|
// Step 3: Jump to new or unchanged firmware
|
||||||
self.jump_to_firmware();
|
self.jump_to_firmware();
|
||||||
|
@ -131,8 +123,13 @@ impl<
|
||||||
progress
|
progress
|
||||||
);
|
);
|
||||||
|
|
||||||
let exchange_result =
|
let exchange_result = self
|
||||||
self.exchange_banks_with_start(progress.a, progress.b, progress.page_index);
|
.swap
|
||||||
|
.exchange::<InternalMemory, HardwareState, INTERNAL_PAGE_SIZE>(
|
||||||
|
&mut self.internal_memory,
|
||||||
|
&mut self.state,
|
||||||
|
progress,
|
||||||
|
);
|
||||||
|
|
||||||
if exchange_result.is_ok() {
|
if exchange_result.is_ok() {
|
||||||
let state = self.state.read().update;
|
let state = self.state.read().update;
|
||||||
|
@ -168,7 +165,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
|
||||||
|
@ -198,101 +195,20 @@ impl<
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exchange_banks(&mut self, a: Bank, b: Bank) -> Result<(), MemoryError> {
|
fn exchange_banks(&mut self, a: Bank, b: Bank) -> Result<(), MemoryError> {
|
||||||
self.exchange_banks_with_start(a, b, 0)
|
self.swap
|
||||||
|
.exchange::<InternalMemory, HardwareState, INTERNAL_PAGE_SIZE>(
|
||||||
|
&mut self.internal_memory,
|
||||||
|
&mut self.state,
|
||||||
|
ExchangeProgress {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
page_index: 0,
|
||||||
|
recovering: false,
|
||||||
|
step: ExchangeStep::AToScratch,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// Jump to the firmware image marked as bootable
|
||||||
fn jump_to_firmware(&mut self) -> ! {
|
fn jump_to_firmware(&mut self) -> ! {
|
||||||
let app_exec_image = self.config.boot_bank;
|
let app_exec_image = self.config.boot_bank;
|
||||||
|
|
|
@ -11,10 +11,12 @@
|
||||||
//!* Automatic Linker Script generation based on a Section/Parition Description in Rust Code
|
//!* Automatic Linker Script generation based on a Section/Parition Description in Rust Code
|
||||||
|
|
||||||
mod boot;
|
mod boot;
|
||||||
|
|
||||||
/// Implementations for use in the bootloader
|
/// Implementations for use in the bootloader
|
||||||
pub use boot::MoonbootBoot;
|
pub use boot::MoonbootBoot;
|
||||||
|
|
||||||
mod manager;
|
mod manager;
|
||||||
|
|
||||||
/// Implementations for use in the firmware
|
/// Implementations for use in the firmware
|
||||||
pub use manager::MoonbootManager;
|
pub use manager::MoonbootManager;
|
||||||
|
|
||||||
|
@ -22,6 +24,8 @@ 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;
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ impl<
|
||||||
|
|
||||||
log::trace!("New state: {:?}", current_state);
|
log::trace!("New state: {:?}", current_state);
|
||||||
|
|
||||||
self.state.write(current_state)
|
self.state.write(¤t_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upgrade firmware verifiying the given signature over the size of size.
|
// Upgrade firmware verifiying the given signature over the size of size.
|
||||||
|
@ -107,7 +107,7 @@ impl<
|
||||||
|
|
||||||
current_state.update = Update::Request(bank);
|
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!");
|
log::info!("Stored update request, jumping to bootloader! Geronimo!");
|
||||||
|
|
||||||
|
|
27
src/state.rs
27
src/state.rs
|
@ -47,6 +47,20 @@ pub enum UpdateError {
|
||||||
InvalidSignature,
|
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(feature = "ram-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
|
/// 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))]
|
||||||
|
@ -59,6 +73,8 @@ pub struct ExchangeProgress {
|
||||||
pub(crate) b: Bank,
|
pub(crate) b: Bank,
|
||||||
/// Page the operation has last copied
|
/// Page the operation has last copied
|
||||||
pub(crate) page_index: u32,
|
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)
|
/// Whether this exchange resulted from a Request (false) or a Revert (true)
|
||||||
pub(crate) recovering: bool,
|
pub(crate) recovering: bool,
|
||||||
}
|
}
|
||||||
|
@ -84,7 +100,7 @@ pub trait State {
|
||||||
/// Read the shared state
|
/// Read the shared state
|
||||||
fn read(&mut self) -> MoonbootState;
|
fn read(&mut self) -> MoonbootState;
|
||||||
/// Write the new state to the shared state
|
/// Write the new state to the shared state
|
||||||
fn write(&mut self, data: MoonbootState) -> Result<(), ()>;
|
fn write(&mut self, data: &MoonbootState) -> Result<(), ()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Size of the serialized state
|
/// Size of the serialized state
|
||||||
|
@ -125,8 +141,7 @@ pub mod ram {
|
||||||
|
|
||||||
let checksum = checksum(unsafe { &_moonboot_state_data_start });
|
let checksum = checksum(unsafe { &_moonboot_state_data_start });
|
||||||
if crc == checksum {
|
if crc == checksum {
|
||||||
let data =
|
let data = MoonbootState::deserialize_from(unsafe { &_moonboot_state_data_start });
|
||||||
MoonbootState::deserialize_from(unsafe { &_moonboot_state_data_start });
|
|
||||||
log::trace!("CRC Match! {}: {:?}", crc, data);
|
log::trace!("CRC Match! {}: {:?}", crc, data);
|
||||||
return data;
|
return data;
|
||||||
} else {
|
} else {
|
||||||
|
@ -138,13 +153,11 @@ pub mod ram {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, data: MoonbootState) -> Result<(), ()> {
|
fn write(&mut self, data: &MoonbootState) -> Result<(), ()> {
|
||||||
log::trace!("Writing data {:?}", data);
|
log::trace!("Writing data {:?}", data);
|
||||||
|
|
||||||
unsafe { _moonboot_state_data_start = data.serialize() };
|
unsafe { _moonboot_state_data_start = data.serialize() };
|
||||||
log::trace!("Written data: {:?}", unsafe {
|
log::trace!("Written data: {:?}", unsafe { &_moonboot_state_data_start });
|
||||||
&_moonboot_state_data_start
|
|
||||||
});
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
_moonboot_state_crc_start = checksum(&_moonboot_state_data_start);
|
_moonboot_state_crc_start = checksum(&_moonboot_state_data_start);
|
||||||
|
|
25
src/swap/mod.rs
Normal file
25
src/swap/mod.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
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>;
|
||||||
|
}
|
119
src/swap/ram.rs
Normal file
119
src/swap/ram.rs
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
use embedded_storage::Storage;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
log,
|
||||||
|
state::{ExchangeProgress, State, Update},
|
||||||
|
swap::{MemoryError, Swap},
|
||||||
|
Address,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Ram;
|
||||||
|
|
||||||
|
impl Swap for Ram {
|
||||||
|
fn exchange<InternalMemory: Storage, HardwareState: State, const INTERNAL_PAGE_SIZE: usize>(
|
||||||
|
&mut self,
|
||||||
|
internal_memory: &mut InternalMemory,
|
||||||
|
state: &mut HardwareState,
|
||||||
|
exchange: ExchangeProgress,
|
||||||
|
) -> Result<(), MemoryError> {
|
||||||
|
let ExchangeProgress {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
page_index,
|
||||||
|
step,
|
||||||
|
..
|
||||||
|
} = exchange;
|
||||||
|
|
||||||
|
// 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 :)
|
||||||
|
|
||||||
|
let mut last_state = state.read();
|
||||||
|
|
||||||
|
// 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(_));
|
||||||
|
|
||||||
|
// TODO: Fix
|
||||||
|
let a_location = a.location;
|
||||||
|
let b_location = b.location;
|
||||||
|
|
||||||
|
for page_index in page_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
|
||||||
|
);
|
||||||
|
|
||||||
|
internal_memory
|
||||||
|
.read(a_location + offset, &mut page_a_buf)
|
||||||
|
.map_err(|_| MemoryError::ReadFailure)?;
|
||||||
|
internal_memory
|
||||||
|
.read(b_location + offset, &mut page_b_buf)
|
||||||
|
.map_err(|_| MemoryError::ReadFailure)?;
|
||||||
|
internal_memory
|
||||||
|
.write(a_location + offset, &page_b_buf)
|
||||||
|
.map_err(|_| MemoryError::WriteFailure)?;
|
||||||
|
internal_memory
|
||||||
|
.write(b_location + offset, &page_a_buf)
|
||||||
|
.map_err(|_| MemoryError::WriteFailure)?;
|
||||||
|
|
||||||
|
// Store the exchange progress
|
||||||
|
|
||||||
|
last_state.update = Update::Exchanging(ExchangeProgress {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
recovering,
|
||||||
|
page_index,
|
||||||
|
step,
|
||||||
|
});
|
||||||
|
|
||||||
|
state
|
||||||
|
.write(&last_state)
|
||||||
|
.map_err(|_| MemoryError::WriteFailure)?;
|
||||||
|
}
|
||||||
|
// TODO: Fit this into the while loop
|
||||||
|
if remaining_page_length > 0 {
|
||||||
|
let offset = full_pages * INTERNAL_PAGE_SIZE as u32;
|
||||||
|
|
||||||
|
internal_memory
|
||||||
|
.read(
|
||||||
|
a.location + offset,
|
||||||
|
&mut page_a_buf[0..remaining_page_length],
|
||||||
|
)
|
||||||
|
.map_err(|_| MemoryError::ReadFailure)?;
|
||||||
|
internal_memory
|
||||||
|
.read(
|
||||||
|
b.location + offset,
|
||||||
|
&mut page_b_buf[0..remaining_page_length],
|
||||||
|
)
|
||||||
|
.map_err(|_| MemoryError::ReadFailure)?;
|
||||||
|
internal_memory
|
||||||
|
.write(a.location + offset, &page_a_buf[0..remaining_page_length])
|
||||||
|
.map_err(|_| MemoryError::WriteFailure)?;
|
||||||
|
internal_memory
|
||||||
|
.write(b.location + offset, &page_b_buf[0..remaining_page_length])
|
||||||
|
.map_err(|_| MemoryError::WriteFailure)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
128
src/swap/scratch.rs
Normal file
128
src/swap/scratch.rs
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
use core::ops::Range;
|
||||||
|
|
||||||
|
use embedded_storage::Storage;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
log,
|
||||||
|
state::{ExchangeProgress, ExchangeStep, State, Update},
|
||||||
|
swap::{MemoryError, Swap},
|
||||||
|
Address,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Scratch<'a> {
|
||||||
|
pub pages: &'a [Range<Address>],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Swap for Scratch<'a> {
|
||||||
|
fn exchange<InternalMemory: Storage, HardwareState: State, const INTERNAL_PAGE_SIZE: usize>(
|
||||||
|
&mut self,
|
||||||
|
internal_memory: &mut InternalMemory,
|
||||||
|
state: &mut HardwareState,
|
||||||
|
exchange: ExchangeProgress,
|
||||||
|
) -> Result<(), MemoryError> {
|
||||||
|
let ExchangeProgress {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
page_index,
|
||||||
|
mut step,
|
||||||
|
..
|
||||||
|
} = exchange;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
assert_eq!(remaining_page_length, 0);
|
||||||
|
|
||||||
|
let mut ram_buf = [0_u8; INTERNAL_PAGE_SIZE];
|
||||||
|
|
||||||
|
let mut last_state = state.read();
|
||||||
|
|
||||||
|
// 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 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 as usize % self.pages.len();
|
||||||
|
let scratch_location = self.pages[scratch_index].start;
|
||||||
|
|
||||||
|
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(|_| MemoryError::WriteFailure)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
match step {
|
||||||
|
ExchangeStep::AToScratch => {
|
||||||
|
internal_memory
|
||||||
|
.read(a_location, &mut ram_buf)
|
||||||
|
.map_err(|_| MemoryError::ReadFailure)?;
|
||||||
|
internal_memory
|
||||||
|
.write(scratch_location, &ram_buf)
|
||||||
|
.map_err(|_| MemoryError::WriteFailure)?;
|
||||||
|
step = ExchangeStep::BToA;
|
||||||
|
}
|
||||||
|
ExchangeStep::BToA => {
|
||||||
|
internal_memory
|
||||||
|
.read(b_location, &mut ram_buf)
|
||||||
|
.map_err(|_| MemoryError::ReadFailure)?;
|
||||||
|
internal_memory
|
||||||
|
.write(a_location, &ram_buf)
|
||||||
|
.map_err(|_| MemoryError::WriteFailure)?;
|
||||||
|
step = ExchangeStep::ScratchToB;
|
||||||
|
}
|
||||||
|
ExchangeStep::ScratchToB => {
|
||||||
|
internal_memory
|
||||||
|
.read(scratch_location, &mut ram_buf)
|
||||||
|
.map_err(|_| MemoryError::ReadFailure)?;
|
||||||
|
internal_memory
|
||||||
|
.write(b_location, &ram_buf)
|
||||||
|
.map_err(|_| MemoryError::WriteFailure)?;
|
||||||
|
step = ExchangeStep::AToScratch;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue