Skip to main content

tinyboot_core/
core.rs

1use crate::protocol;
2use crate::traits::boot::{BootCtl, BootMetaStore, Platform, Storage, Transport};
3use crate::traits::{BootMode, BootState};
4
5/// Bootloader entry point. Checks boot state, validates the app, and either
6/// boots the application or enters the protocol loop for firmware updates.
7pub struct Core<T, S, B, C, const BUF: usize>
8where
9    T: Transport,
10    S: Storage,
11    B: BootMetaStore,
12    C: BootCtl,
13{
14    platform: Platform<T, S, B, C>,
15}
16
17impl<T, S, B, C, const BUF: usize> Core<T, S, B, C, BUF>
18where
19    T: Transport,
20    S: Storage,
21    B: BootMetaStore,
22    C: BootCtl,
23{
24    /// Create a new bootloader core from a platform implementation.
25    #[inline(always)]
26    pub fn new(platform: Platform<T, S, B, C>) -> Self {
27        Core { platform }
28    }
29
30    /// Run the bootloader. Checks boot state, validates the app, and either
31    /// boots the application or enters the protocol loop. Does not return.
32    #[inline(always)]
33    pub fn run(mut self) -> ! {
34        match self.check_boot_state() {
35            Ok(BootMode::App) => self.platform.ctl.system_reset(BootMode::App),
36            Ok(BootMode::Bootloader) | Err(_) => self.enter_bootloader(),
37        }
38    }
39
40    fn check_boot_state(&mut self) -> Result<BootMode, B::Error> {
41        if self.platform.ctl.is_boot_requested() {
42            return Ok(BootMode::Bootloader);
43        }
44
45        match self.platform.boot_meta.boot_state() {
46            BootState::Idle => {}
47            BootState::Updating => return Ok(BootMode::Bootloader),
48            BootState::Validating => {
49                if !self.platform.boot_meta.has_trials() {
50                    return Ok(BootMode::Bootloader);
51                }
52                self.platform.boot_meta.consume_trial()?;
53            }
54        }
55
56        if !self.validate_app() {
57            return Ok(BootMode::Bootloader);
58        }
59
60        Ok(BootMode::App)
61    }
62
63    /// Validate the app image. The CRC covers only `app_size` bytes
64    /// (the actual firmware), not the entire flash region.
65    fn validate_app(&self) -> bool {
66        let stored = self.platform.boot_meta.app_checksum();
67        if stored != 0xFFFF {
68            use tinyboot_protocol::crc::{CRC_INIT, crc16};
69            let sz = self.platform.boot_meta.app_size() as usize;
70            // SAFETY: checksum != 0xFFFF means meta was previously written
71            // by a Verify that validated app_size against capacity.
72            return crc16(CRC_INIT, unsafe {
73                self.platform.storage.as_slice().get_unchecked(..sz)
74            }) == stored;
75        }
76        // No CRC stored (virgin/debugger-flashed) — check if app exists
77        let data = self.platform.storage.as_slice();
78        data.len() >= 4
79            && unsafe { core::ptr::read_volatile(data.as_ptr() as *const u32) } != 0xFFFF_FFFF
80    }
81
82    #[inline(always)]
83    fn enter_bootloader(&mut self) -> ! {
84        self.platform.storage.unlock();
85
86        let mut d = protocol::Dispatcher::<_, _, _, _, BUF>::new(&mut self.platform);
87
88        loop {
89            let _ = d.dispatch();
90        }
91    }
92}