moonboot/src/state.rs

155 lines
5.5 KiB
Rust
Raw Normal View History

2022-04-19 08:36:20 +00:00
use crate::hardware::Bank;
use crate::log;
use crc::{Crc, CRC_32_CKSUM};
#[cfg(feature = "defmt")]
use defmt::Format;
#[cfg(feature = "ram-state")]
use desse::{Desse, DesseSized};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
// Decision making for the bootloader
// TODO: Hash + Signature? Should be done on download I think! This way, the algorithms can be
// exchanged via software updates easily
#[cfg_attr(feature = "use-defmt", derive(Format))]
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))]
#[derive(Debug, PartialEq, Eq)]
pub enum Update {
// No update requested, just jump to the application
None,
// Exchange the current boot image with the one from image specified as index, a signature to
// verify against and the size of the firmware image to make the firmware signature
// verification succeed
Request(Bank),
// Revert the current boot image to the from image specified as index
Revert(Bank),
// An Exchange Operation is in Progress or was interrupted
Exchanging(ExchangeProgress),
// An Error during the update has occured!
Error(UpdateError),
}
#[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)]
// Errors that can occur during update
pub enum UpdateError {
// A wrong Image Index has been specified
InvalidImageIndex,
// Failed to exchange the new image with the old one
ImageExchangeFailed,
// Something f'ed up the internal state
InvalidState,
// The Signature provided does not match the PublicKey or Image.
InvalidSignature,
}
// Store the progress of the current exchange operation
#[cfg_attr(feature = "use-defmt", derive(Format))]
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExchangeProgress {
// Bank the update is coming from
pub(crate) a: Bank,
// Bank the update is going to
pub(crate) b: Bank,
// Page the operation has last copied
pub(crate) page_index: u32,
// Whether this exchange resulted from a Request (false) or a Revert (true)
pub(crate) recovering: bool,
}
// Struct used to store the state of the bootloader situation in NVM
#[cfg_attr(feature = "use-defmt", derive(Format))]
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "ram-state", derive(Desse, DesseSized))]
#[derive(Debug)]
pub struct MoonshineState {
// If set Request, an Update is requested. This will exchange the two images, set the update
// state to Revert and start the application. The application then has to set this state to
// None and store it. If something went wrong and the boot attempt results in a restart of
// the bootloader, the bootloader starts with this variable set to Revert and thus exchanges
// the two images again, doing a downgrade because of a failed boot
pub update: Update,
}
pub trait State {
fn read(&mut self) -> MoonshineState;
fn write(&mut self, data: MoonshineState) -> Result<(), ()>;
}
pub const STATE_SERIALIZED_MAX_SIZE: usize = MoonshineState::SIZE;
pub type StateCrcType = u32;
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 _moonshine_state_crc_start: StateCrcType;
static mut _moonshine_state_data_start: [u8; STATE_SERIALIZED_MAX_SIZE];
// TODO: Move these as normal variables to linker sections via #[link] macro?
}
impl State for RamState {
fn read(&mut self) -> MoonshineState {
let crc = unsafe { _moonshine_state_crc_start };
log::info!(
"Reading data with len: {}, CRC: {}",
STATE_SERIALIZED_MAX_SIZE,
crc
);
let checksum = checksum(unsafe { &_moonshine_state_data_start });
if crc == checksum {
let data =
MoonshineState::deserialize_from(unsafe { &_moonshine_state_data_start });
log::trace!("CRC Match! {}: {:?}", crc, data);
return data;
} else {
log::trace!("CRC Mismatch! {} vs {}", crc, checksum);
}
MoonshineState {
update: Update::None,
}
}
fn write(&mut self, data: MoonshineState) -> Result<(), ()> {
log::trace!("Writing data {:?}", data);
unsafe { _moonshine_state_data_start = data.serialize() };
log::trace!("Written data: {:?}", unsafe {
&_moonshine_state_data_start
});
unsafe {
_moonshine_state_crc_start = checksum(&_moonshine_state_data_start);
}
log::info!(
"Written len: {}, checksum: {}",
STATE_SERIALIZED_MAX_SIZE,
unsafe { _moonshine_state_crc_start }
);
Ok(())
}
}
}