use crate::{ hardware::{processor::Processor, Config}, state::{State, Update}, }; 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 MoonshineManager< InternalMemory: Storage, HardwareState: State, CPU: Processor, const INTERNAL_PAGE_SIZE: usize, > { config: Config, internal_memory: InternalMemory, state: HardwareState, processor: CPU, } impl< InternalMemory: Storage, HardwareState: State, CPU: Processor, const INTERNAL_PAGE_SIZE: usize, > MoonshineManager { pub fn new( config: Config, internal_memory: InternalMemory, state: HardwareState, processor: CPU, ) -> MoonshineManager { Self { config, internal_memory, 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) } /// 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(); log::info!( "Application running, marking boot as successful. Current state: {:?}", current_state ); current_state.update = match current_state.update { Update::None => { log::info!("No Update was done."); Update::None } Update::Revert(_) => { log::info!("Software was updated, marking as successful."); Update::None } _ => { log::error!("There is an update queued, but it has not been installed yet. Did you skip the bootloader?"); return Err(()); } }; log::trace!("New state: {:?}", current_state); self.state.write(current_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 { // 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(); if current_state.update != Update::None { log::warn!( "There is already an update in progress or queued: {:?}", current_state.update ); } current_state.update = Update::Request(bank); self.state.write(current_state)?; log::info!("Stored update request, jumping to bootloader! Geronimo!"); let bootloader_address = self.config.bootloader_bank.location; log::info!("Executing pre jump handler."); extern "Rust" { fn _moonboots_pre_jump(); } unsafe { _moonboots_pre_jump(); } self.processor.do_jump(bootloader_address) } } impl< InternalMemory: Storage, HardwareState: State, CPU: Processor, const INTERNAL_PAGE_SIZE: usize, > core::convert::AsRef<[u8]> for MoonshineManager { #[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 MoonshineManager { 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 MoonshineManager { 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(|_| ()) } } } } }