Reworked errors

This commit is contained in:
Wouter Geraedts 2022-07-11 18:50:55 +02:00
parent e2d1ce797b
commit 1cd3c4f5be
4 changed files with 183 additions and 194 deletions

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
hardware::processor::Processor, hardware::processor::Processor,
hardware::{Bank, Config}, hardware::{Bank, Config},
state::{Exchange, ExchangeProgress, ExchangeStep, MemoryError, State, Update, UpdateError}, state::{Exchange, ExchangeError, ExchangeProgress, ExchangeStep, State, Update, UpdateError},
}; };
use embedded_storage::Storage; use embedded_storage::Storage;
@ -58,13 +58,13 @@ impl<
} }
/// Execute the update and boot logic of the bootloader /// 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, STATE::Error> {
// TODO: consider error handling // TODO: consider error handling
log::info!("Booting with moonboot!"); log::info!("Booting with moonboot!");
self.processor.setup(&self.config); self.processor.setup(&self.config);
let mut state = self.state.read(); let mut state = self.state.read()?;
log::info!("Old State: {:?}", state); log::info!("Old State: {:?}", state);
@ -73,7 +73,7 @@ impl<
Update::None => self.handle_none(), Update::None => self.handle_none(),
Update::Request(bank) => self.handle_request(bank), Update::Request(bank) => self.handle_request(bank),
Update::Revert(bank) => self.handle_revert(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), Update::Error(err) => Update::Error(err),
}; };
@ -116,7 +116,7 @@ impl<
// Handle a case of power interruption or similar, which lead to a exchange_banks being // Handle a case of power interruption or similar, which lead to a exchange_banks being
// interrupted. // interrupted.
fn handle_exchanging(&mut self, progress: ExchangeProgress) -> Update { fn handle_exchanging(&mut self, progress: ExchangeProgress) -> Result<Update, STATE::Error> {
log::error!( log::error!(
"Firmware Update was interrupted! Trying to recover with exchange operation: {:?}", "Firmware Update was interrupted! Trying to recover with exchange operation: {:?}",
progress progress
@ -130,8 +130,8 @@ impl<
progress, progress,
); );
if exchange_result.is_ok() { Ok(if exchange_result.is_ok() {
let state = self.state.read().update; let state = self.state.read()?.update;
match state { match state {
Update::Exchanging(progress) => { Update::Exchanging(progress) => {
if progress.recovering { if progress.recovering {
@ -148,7 +148,7 @@ impl<
exchange_result exchange_result
); );
Update::Error(UpdateError::ImageExchangeFailed) Update::Error(UpdateError::ImageExchangeFailed)
} })
} }
// Revert the bootable image with the image in index new_firmware. Returns Revert on success if // Revert the bootable image with the image in index new_firmware. Returns Revert on success if
@ -193,7 +193,11 @@ impl<
} }
} }
fn exchange_banks(&mut self, a: Bank, b: Bank) -> Result<(), MemoryError> { fn exchange_banks(
&mut self,
a: Bank,
b: Bank,
) -> Result<(), ExchangeError<STORAGE::Error, STATE::Error, EXCHANGE::OtherError>> {
self.exchange self.exchange
.exchange::<STORAGE, STATE, INTERNAL_PAGE_SIZE>( .exchange::<STORAGE, STATE, INTERNAL_PAGE_SIZE>(
&mut self.storage, &mut self.storage,

View file

@ -3,55 +3,67 @@ use crate::{
state::{State, Update}, state::{State, Update},
}; };
use embedded_storage::{ReadStorage, Storage}; use embedded_storage::Storage;
use crate::log; use crate::log;
/// Instantiate this in your application to enable mutation of the State specified in this and jump /// Instantiate this in your application to enable mutation of the State specified in this and jump
/// to the bootloader to apply any updates. /// to the bootloader to apply any updates.
pub struct MoonbootManager< pub struct MoonbootManager<
InternalMemory: Storage, STORAGE: Storage,
HardwareState: State, STATE: State,
CPU: Processor, PROCESSOR: Processor,
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,
} }
impl< pub struct InitError;
InternalMemory: Storage,
HardwareState: State, pub enum MarkError<E> {
CPU: Processor, UpdateQueuedButNotInstalled,
const INTERNAL_PAGE_SIZE: usize, State(E),
> MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> }
impl<STORAGE: Storage, STATE: State, PROCESSOR: Processor, const INTERNAL_PAGE_SIZE: usize>
MoonbootManager<STORAGE, STATE, PROCESSOR, INTERNAL_PAGE_SIZE>
{ {
pub fn new( pub fn new(
config: Config, config: Config,
internal_memory: InternalMemory, storage: STORAGE,
state: HardwareState, state: STATE,
processor: CPU, processor: PROCESSOR,
) -> MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> { ) -> Result<MoonbootManager<STORAGE, STATE, PROCESSOR, INTERNAL_PAGE_SIZE>, InitError> {
Self { if config.update_bank.size > config.boot_bank.size {
log::error!(
"Requested update bank {:?} is larger than boot bank {:?}",
bank,
self.config.boot_bank
);
return Err(InitError);
}
Ok(Self {
config, config,
internal_memory, storage,
state, state,
processor, processor,
} })
} }
/// Destroy this instance of the boot manager and return access to the hardware peripheral /// Destroy this instance of the boot manager 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)
} }
/// Run this immediately after booting your new image successfully to mark the boot as /// 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 /// succesful. If you do not do this, any reset will cause the bootloader to restore to the
/// previous firmware image. /// previous firmware image.
pub fn mark_boot_successful(&mut self) -> Result<(), ()> { pub fn mark_boot_successful(&mut self) -> Result<(), MarkError<STATE::Error>> {
let mut current_state = self.state.read(); let mut current_state = self.state.read().map_err(MarkError::State)?;
log::info!( log::info!(
"Application running, marking boot as successful. Current state: {:?}", "Application running, marking boot as successful. Current state: {:?}",
@ -69,34 +81,24 @@ impl<
} }
_ => { _ => {
log::error!("There is an update queued, but it has not been installed yet. Did you skip the bootloader?"); 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); log::trace!("New state: {:?}", current_state);
self.state.write(&current_state) self.state.write(&current_state).map_err(MarkError::State)
} }
// Upgrade firmware verifiying the given signature over the size of size. // 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) // 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, STATE::Error> {
// Apply the update stored in the update bank // Apply the update stored in the update bank
let bank = self.config.update_bank; let bank = self.config.update_bank;
// TODO: Check size value!
log::info!("Update requested on slot {:?}", bank); log::info!("Update requested on slot {:?}", bank);
if bank.size > self.config.boot_bank.size { let mut current_state = self.state.read()?;
log::error!(
"Requested update bank {:?} is larger than boot bank {:?}",
bank,
self.config.boot_bank
);
return Err(());
}
let mut current_state = self.state.read();
if current_state.update != Update::None { if current_state.update != Update::None {
log::warn!( log::warn!(
@ -125,78 +127,78 @@ impl<
} }
} }
/// Easily get read access to the update bank // /// Easily get read access to the update bank
impl< // impl<
InternalMemory: Storage, // InternalMemory: Storage,
HardwareState: State, // HardwareState: State,
CPU: Processor, // PROCESSOR: Processor,
const INTERNAL_PAGE_SIZE: usize, // const INTERNAL_PAGE_SIZE: usize,
> core::convert::AsRef<[u8]> // > core::convert::AsRef<[u8]>
for MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> // for MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
{ // {
#[inline] // #[inline]
fn as_ref(&self) -> &[u8] { // fn as_ref(&self) -> &[u8] {
unsafe { // unsafe {
core::slice::from_raw_parts( // core::slice::from_raw_parts(
self.config.update_bank.location as *const u8, // self.config.update_bank.location as *const u8,
self.config.update_bank.size as usize, // self.config.update_bank.size as usize,
) // )
} // }
} // }
} // }
/// Read Access to the current update target slot // /// Read Access to the current update target slot
impl< // impl<
InternalMemory: Storage, // InternalMemory: Storage,
HardwareState: State, // HardwareState: State,
CPU: Processor, // CPU: Processor,
const INTERNAL_PAGE_SIZE: usize, // const INTERNAL_PAGE_SIZE: usize,
> ReadStorage for MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE> // > ReadStorage for MoonbootManager<InternalMemory, HardwareState, CPU, INTERNAL_PAGE_SIZE>
{ // {
type Error = (); // TODO // type Error = (); // TODO
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { // 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. // 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 { // if offset > bank.size || offset + bytes.len() as u32 > bank.size {
Err(()) // TODO: We want better error types! // Err(()) // TODO: We want better error types!
} else { // } else {
// TODO! fix // // TODO! fix
let bank_start = bank.location; // let bank_start = bank.location;
log::info!("Writing at {:x}[{:x}]", bank_start, offset); // log::info!("Writing at {:x}[{:x}]", bank_start, offset);
match bank.memory_unit { // match bank.memory_unit {
crate::hardware::MemoryUnit::Internal => { // crate::hardware::MemoryUnit::Internal => {
{ self.internal_memory.read(bank_start + offset, bytes) }.map_err(|_| ()) // { self.storage.read(bank_start + offset, bytes) }.map_err(|_| ())
} // }
} // }
} // }
} // }
fn capacity(&self) -> usize { // fn capacity(&self) -> usize {
self.config.update_bank.size as usize // self.config.update_bank.size as usize
} // }
} // }
/// Write Access to the current update target slot // /// Write Access to the current update target slot
impl< // impl<
InternalMemory: Storage, // InternalMemory: Storage,
HardwareState: State, // HardwareState: State,
CPU: Processor, // CPU: Processor,
const INTERNAL_PAGE_SIZE: usize, // const INTERNAL_PAGE_SIZE: usize,
> Storage for MoonbootManager<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> { // 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. // 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 { // if offset > bank.size || offset + bytes.len() as u32 > bank.size {
Err(()) // TODO: We want better error types! // Err(()) // TODO: We want better error types!
} else { // } else {
// TODO! fix // // TODO! fix
let bank_start = bank.location; // let bank_start = bank.location;
log::info!("Writing at {:x}[{:x}]", bank_start, offset); // log::info!("Writing at {:x}[{:x}]", bank_start, offset);
match bank.memory_unit { // match bank.memory_unit {
crate::hardware::MemoryUnit::Internal => { // crate::hardware::MemoryUnit::Internal => {
{ self.internal_memory.write(bank_start + offset, bytes) }.map_err(|_| ()) // { self.internal_memory.write(bank_start + offset, bytes) }.map_err(|_| ())
} // }
} // }
} // }
} // }
} // }

View file

@ -6,24 +6,22 @@ pub mod scratch;
use embedded_storage::Storage; use embedded_storage::Storage;
/// Error occured during memory access pub enum ExchangeError<STORAGE, STATE, OTHER> {
#[cfg_attr(feature = "use-defmt", derive(Format))] Storage(STORAGE),
#[derive(Debug)] State(STATE),
pub enum MemoryError { Other(OTHER),
BankSizeNotEqual,
BankSizeZero,
ReadFailure,
WriteFailure,
} }
/// Abstraction for the exchange operation of the current state. /// Abstraction for the exchange operation of the current state.
pub trait Exchange { pub trait Exchange {
type OtherError;
fn exchange<STORAGE: Storage, STATE: State, const INTERNAL_PAGE_SIZE: usize>( fn exchange<STORAGE: Storage, STATE: State, const INTERNAL_PAGE_SIZE: usize>(
&mut self, &mut self,
internal_memory: &mut STORAGE, internal_memory: &mut STORAGE,
state: &mut STATE, state: &mut STATE,
progress: ExchangeProgress, progress: ExchangeProgress,
) -> Result<(), MemoryError>; ) -> Result<(), ExchangeError<STORAGE::Error, STATE::Error, Self::OtherError>>;
} }
use crate::hardware::Bank; use crate::hardware::Bank;
@ -139,10 +137,12 @@ pub struct MoonbootState {
/// RAM. As long as you don't want to perform update download, power cycle the device, and then /// 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. /// apply the update, storing it in volatile memory is fine.
pub trait State { pub trait State {
type Error;
/// Read the shared state /// 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 /// 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 /// Size of the serialized state

View file

@ -15,7 +15,9 @@ extern "C" {
} }
impl State for RamState { impl State for RamState {
fn read(&mut self) -> MoonbootState { type Error = void::Void;
fn read(&mut self) -> Result<MoonbootState, void::Void> {
let crc = unsafe { _moonboot_state_crc_start }; let crc = unsafe { _moonboot_state_crc_start };
log::info!( log::info!(
@ -28,17 +30,16 @@ impl State for RamState {
if crc == checksum { if crc == checksum {
let data = MoonbootState::deserialize_from(unsafe { &_moonboot_state_data_start }); let data = MoonbootState::deserialize_from(unsafe { &_moonboot_state_data_start });
log::trace!("CRC Match! {}: {:?}", crc, data); log::trace!("CRC Match! {}: {:?}", crc, data);
return data; Ok(data)
} else { } else {
log::trace!("CRC Mismatch! {} vs {}", crc, checksum); log::trace!("CRC Mismatch! {} vs {}", crc, checksum);
} Ok(MoonbootState {
update: Update::None,
MoonbootState { })
update: Update::None,
} }
} }
fn write(&mut self, data: &MoonbootState) -> Result<(), ()> { fn write(&mut self, data: &MoonbootState) -> Result<(), Self::Error> {
log::trace!("Writing data {:?}", data); log::trace!("Writing data {:?}", data);
unsafe { _moonboot_state_data_start = data.serialize() }; unsafe { _moonboot_state_data_start = data.serialize() };
@ -58,28 +59,21 @@ impl State for RamState {
} }
impl Exchange for RamState { impl Exchange for RamState {
fn exchange<InternalMemory: Storage, HardwareState: State, const INTERNAL_PAGE_SIZE: usize>( type OtherError = void::Void;
fn exchange<STORAGE: Storage, STATE: State, const INTERNAL_PAGE_SIZE: usize>(
&mut self, &mut self,
internal_memory: &mut InternalMemory, storage: &mut STORAGE,
state: &mut HardwareState, state: &mut STATE,
exchange: ExchangeProgress, progress: ExchangeProgress,
) -> Result<(), MemoryError> { ) -> Result<(), ExchangeError<STORAGE::Error, STATE::Error, Self::OtherError>> {
let ExchangeProgress { let ExchangeProgress {
a, a,
b, b,
page_index, page_index,
step, step,
.. ..
} = exchange; } = progress;
// 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 size = a.size; // Both are equal
@ -88,41 +82,36 @@ impl Exchange for RamState {
let mut page_a_buf = [0_u8; 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 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(); 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 // Set this in the exchanging part to know whether we are in a recovery process from a
// failed update or on the initial update // failed update or on the initial update
let recovering = matches!(last_state.update, Update::Revert(_)); 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 { for page_index in page_index..full_pages {
let offset = page_index * INTERNAL_PAGE_SIZE as u32; let offset = page_index * INTERNAL_PAGE_SIZE as u32;
let a_location = a.location + offset;
let b_location = b.location + offset;
log::trace!( log::trace!(
"Exchange: Page {}, from a ({}) to b ({})", "Exchange: Page {}, from a ({}) to b ({})",
page_index, page_index,
a_location + offset, a_location,
b_location + offset b_location
); );
internal_memory storage
.read(a_location + offset, &mut page_a_buf) .read(a_location, &mut page_a_buf)
.map_err(|_| MemoryError::ReadFailure)?; .map_err(ExchangeError::Storage)?;
internal_memory storage
.read(b_location + offset, &mut page_b_buf) .read(b_location, &mut page_b_buf)
.map_err(|_| MemoryError::ReadFailure)?; .map_err(ExchangeError::Storage)?;
internal_memory storage
.write(a_location + offset, &page_b_buf) .write(a_location, &page_b_buf)
.map_err(|_| MemoryError::WriteFailure)?; .map_err(ExchangeError::Storage)?;
internal_memory storage
.write(b_location + offset, &page_a_buf) .write(b_location, &page_a_buf)
.map_err(|_| MemoryError::WriteFailure)?; .map_err(ExchangeError::Storage)?;
// Store the exchange progress // Store the exchange progress
@ -134,32 +123,26 @@ impl Exchange for RamState {
step, step,
}); });
state state.write(&last_state).map_err(ExchangeError::State)?;
.write(&last_state)
.map_err(|_| MemoryError::WriteFailure)?;
} }
// TODO: Fit this into the while loop // TODO: Fit this into the while loop
if remaining_page_length > 0 { if remaining_page_length > 0 {
let offset = full_pages * INTERNAL_PAGE_SIZE as u32; let offset = full_pages * INTERNAL_PAGE_SIZE as u32;
let a_location = a.location + offset;
let b_location = b.location + offset;
internal_memory storage
.read( .read(a_location, &mut page_a_buf[0..remaining_page_length])
a.location + offset, .map_err(ExchangeError::Storage)?;
&mut page_a_buf[0..remaining_page_length], storage
) .read(b_location, &mut page_b_buf[0..remaining_page_length])
.map_err(|_| MemoryError::ReadFailure)?; .map_err(ExchangeError::Storage)?;
internal_memory storage
.read( .write(a_location, &page_a_buf[0..remaining_page_length])
b.location + offset, .map_err(ExchangeError::Storage)?;
&mut page_b_buf[0..remaining_page_length], storage
) .write(b_location + offset, &page_b_buf[0..remaining_page_length])
.map_err(|_| MemoryError::ReadFailure)?; .map_err(ExchangeError::Storage)?;
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(()) Ok(())