From ed89811dd75a0dc63acaf443e091435273abf504 Mon Sep 17 00:00:00 2001 From: addrian-77 Date: Mon, 13 Oct 2025 16:48:29 +0300 Subject: [PATCH 1/8] feat: reshuffle apps + generalizaiton Signed-off-by: addrian-77 --- tbf-parser/src/types.rs | 1 + tockloader-lib/Cargo.toml | 1 + .../src/attributes/app_attributes.rs | 78 +++-- tockloader-lib/src/bootloader_serial.rs | 7 +- tockloader-lib/src/command_impl/erase_apps.rs | 13 + .../src/command_impl/generalized.rs | 49 +-- tockloader-lib/src/command_impl/info.rs | 19 ++ tockloader-lib/src/command_impl/install.rs | 53 ++++ tockloader-lib/src/command_impl/list.rs | 17 + tockloader-lib/src/command_impl/mod.rs | 5 + .../src/command_impl/probers/erase_apps.rs | 32 -- .../src/command_impl/probers/info.rs | 31 -- .../src/command_impl/probers/install.rs | 161 ---------- tockloader-lib/src/command_impl/probers/io.rs | 67 ++++ .../src/command_impl/probers/list.rs | 24 -- .../src/command_impl/probers/mod.rs | 5 +- .../src/command_impl/reshuffle_apps.rs | 291 ++++++++++++++++++ .../src/command_impl/serial/info.rs | 31 -- .../src/command_impl/serial/install.rs | 239 -------------- tockloader-lib/src/command_impl/serial/io.rs | 96 ++++++ .../src/command_impl/serial/list.rs | 25 -- tockloader-lib/src/command_impl/serial/mod.rs | 5 +- tockloader-lib/src/lib.rs | 15 + tockloader-lib/src/tabs/tab.rs | 12 + 24 files changed, 667 insertions(+), 610 deletions(-) create mode 100644 tockloader-lib/src/command_impl/erase_apps.rs create mode 100644 tockloader-lib/src/command_impl/info.rs create mode 100644 tockloader-lib/src/command_impl/install.rs create mode 100644 tockloader-lib/src/command_impl/list.rs delete mode 100644 tockloader-lib/src/command_impl/probers/erase_apps.rs delete mode 100644 tockloader-lib/src/command_impl/probers/info.rs delete mode 100644 tockloader-lib/src/command_impl/probers/install.rs create mode 100644 tockloader-lib/src/command_impl/probers/io.rs delete mode 100644 tockloader-lib/src/command_impl/probers/list.rs create mode 100644 tockloader-lib/src/command_impl/reshuffle_apps.rs delete mode 100644 tockloader-lib/src/command_impl/serial/info.rs delete mode 100644 tockloader-lib/src/command_impl/serial/install.rs create mode 100644 tockloader-lib/src/command_impl/serial/io.rs delete mode 100644 tockloader-lib/src/command_impl/serial/list.rs diff --git a/tbf-parser/src/types.rs b/tbf-parser/src/types.rs index 5f4dca45..07954f14 100644 --- a/tbf-parser/src/types.rs +++ b/tbf-parser/src/types.rs @@ -866,6 +866,7 @@ pub struct TbfHeaderV2 { // Clippy suggests we box TbfHeaderV2. We can't really do that, since // we are runnning under no_std, and I don't think it's that big of a issue. #[allow(clippy::large_enum_variant)] +#[derive(Clone)] pub enum TbfHeader { TbfHeaderV2(TbfHeaderV2), Padding(TbfHeaderV2Base), diff --git a/tockloader-lib/Cargo.toml b/tockloader-lib/Cargo.toml index e89bc42e..ae42f367 100644 --- a/tockloader-lib/Cargo.toml +++ b/tockloader-lib/Cargo.toml @@ -21,3 +21,4 @@ serde = { version = "1.0.210", features = ["derive"] } thiserror = "1.0.63" async-trait = "0.1.88" log = "0.4.27" +itertools = "0.14.0" diff --git a/tockloader-lib/src/attributes/app_attributes.rs b/tockloader-lib/src/attributes/app_attributes.rs index 58c0aa4b..39458b2d 100644 --- a/tockloader-lib/src/attributes/app_attributes.rs +++ b/tockloader-lib/src/attributes/app_attributes.rs @@ -11,22 +11,41 @@ use tokio_serial::SerialStream; use crate::bootloader_serial::{issue_command, Command, Response}; use crate::errors::{TockError, TockloaderError}; +use crate::IOCommands; /// This structure contains all relevant information about a tock application. /// /// All data is stored either within [TbfHeader]s, or [TbfFooter]s. /// /// See also -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct AppAttributes { + pub address: u64, + pub size: u32, + pub index: u8, pub tbf_header: TbfHeader, pub tbf_footers: Vec, + pub installed: bool, + pub is_padding: bool, +} + +impl std::fmt::Display for AppAttributes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}. {} - start: {:#x}, size: {}", + self.index, + self.tbf_header.get_package_name().unwrap_or(""), + self.address, + self.size + ) + } } /// This structure represents a footer of a Tock application. Currently, footers /// only contain credentials, which are used to verify the integrity of the /// application. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TbfFooter { pub credentials: TbfFooterV2Credentials, pub size: u32, @@ -41,10 +60,22 @@ impl TbfFooter { // TODO(george-cosma): Could take advantages of the trait rework impl AppAttributes { - pub(crate) fn new(header_data: TbfHeader, footers_data: Vec) -> AppAttributes { + pub(crate) fn new( + address: u64, + size: u32, + index: u8, + header_data: TbfHeader, + footers_data: Vec, + installed: bool, + ) -> AppAttributes { AppAttributes { + address, + size, + index, tbf_header: header_data, tbf_footers: footers_data, + installed, + is_padding: false, } } @@ -117,13 +148,10 @@ impl AppAttributes { // crash the process. let binary_end_offset = header.get_binary_end(); - match &header { - TbfHeader::TbfHeaderV2(_hd) => {} - _ => { - appaddr += total_size as u64; - continue; - } - }; + if !header.is_app() { + appaddr += total_size as u64; + continue; + } let mut footers: Vec = vec![]; let total_footers_size = total_size - binary_end_offset; @@ -137,9 +165,9 @@ impl AppAttributes { // binary_end_offset`) , even if we overread. let mut appfooter = vec![0u8; (total_footers_size - (footer_offset - binary_end_offset)) as usize]; - + // log::info!("footer init {:?}", appfooter); board_core.read(appaddr + footer_offset as u64, &mut appfooter)?; - + // log::info!("footer read {:?}", appfooter); let footer_info = parse_tbf_footer(&appfooter).map_err(TockError::InvalidAppTbfHeader)?; @@ -150,9 +178,10 @@ impl AppAttributes { footer_offset += footer_info.1 + 4; } - let details: AppAttributes = AppAttributes::new(header, footers); + let details: AppAttributes = + AppAttributes::new(appaddr, total_size, apps_counter, header, footers, true); - apps_details.insert(apps_counter, details); + apps_details.insert(apps_counter.into(), details); apps_counter += 1; appaddr += total_size as u64; } @@ -240,15 +269,13 @@ impl AppAttributes { log::debug!("App #{apps_counter}: Header data: {header_data:?}"); let header = parse_tbf_header(&header_data, tbf_version) .map_err(TockError::InvalidAppTbfHeader)?; + let binary_end_offset = header.get_binary_end(); - match &header { - TbfHeader::TbfHeaderV2(_hd) => {} - _ => { - appaddr += total_size as u64; - continue; - } - }; + if !header.is_app() { + appaddr += total_size as u64; + continue; + } let mut footers: Vec = vec![]; let total_footers_size = total_size - binary_end_offset; @@ -289,12 +316,17 @@ impl AppAttributes { footer_offset += footer_info.1 + 4; } - let details: AppAttributes = AppAttributes::new(header, footers); + let details: AppAttributes = + AppAttributes::new(appaddr, total_size, apps_counter, header, footers, true); - apps_details.insert(apps_counter, details); + apps_details.insert(apps_counter.into(), details); apps_counter += 1; appaddr += total_size as u64; } Ok(apps_details) } + + pub async fn read(&mut self, conn: &mut dyn IOCommands) -> Result, TockloaderError> { + conn.read(self.address, self.size as usize).await + } } diff --git a/tockloader-lib/src/bootloader_serial.rs b/tockloader-lib/src/bootloader_serial.rs index 48107ec1..8893b171 100644 --- a/tockloader-lib/src/bootloader_serial.rs +++ b/tockloader-lib/src/bootloader_serial.rs @@ -5,7 +5,7 @@ // The "X" commands are for external flash use crate::errors::{self, InternalError, TockError}; -use bytes::BytesMut; +use bytes::{BufMut, BytesMut}; use errors::TockloaderError; use std::time::Duration; use tokio::io::{AsyncReadExt, AsyncWriteExt}; @@ -17,7 +17,7 @@ pub const SYNC_MESSAGE: [u8; 3] = [0x00, 0xFC, 0x05]; // "This was chosen as it is infrequent in .bin files" - immesys pub const ESCAPE_CHAR: u8 = 0xFC; -pub const DEFAULT_TIMEOUT: Duration = Duration::from_millis(500); +pub const DEFAULT_TIMEOUT: Duration = Duration::from_millis(5000); #[allow(dead_code)] pub enum Command { @@ -217,7 +217,7 @@ pub async fn issue_command( } if response_len != 0 { - let input = read_bytes(port, response_len, DEFAULT_TIMEOUT).await?; + let mut input = read_bytes(port, response_len, DEFAULT_TIMEOUT).await?; let mut result = Vec::with_capacity(input.len()); // De-escape and add array of read in the bytes @@ -227,6 +227,7 @@ pub async fn issue_command( while i < input.len() { if i + 1 < input.len() && input[i] == ESCAPE_CHAR && input[i + 1] == ESCAPE_CHAR { // Found consecutive ESCAPE_CHAR bytes, add only one + input.put(read_bytes(port, 1, DEFAULT_TIMEOUT).await?); result.push(ESCAPE_CHAR); i += 2; // Skip both bytes } else { diff --git a/tockloader-lib/src/command_impl/erase_apps.rs b/tockloader-lib/src/command_impl/erase_apps.rs new file mode 100644 index 00000000..25a79618 --- /dev/null +++ b/tockloader-lib/src/command_impl/erase_apps.rs @@ -0,0 +1,13 @@ +use async_trait::async_trait; + +use crate::board_settings::BoardSettings; +use crate::connection::TockloaderConnection; +use crate::errors::TockloaderError; +use crate::{CommandEraseApps, IOCommands}; + +#[async_trait] +impl CommandEraseApps for TockloaderConnection { + async fn erase_apps(&mut self, settings: &BoardSettings) -> Result<(), TockloaderError> { + self.write(settings.start_address, [0x0].to_vec()).await + } +} diff --git a/tockloader-lib/src/command_impl/generalized.rs b/tockloader-lib/src/command_impl/generalized.rs index 7fe41d85..22f8f74d 100644 --- a/tockloader-lib/src/command_impl/generalized.rs +++ b/tockloader-lib/src/command_impl/generalized.rs @@ -1,59 +1,42 @@ use async_trait::async_trait; use crate::attributes::app_attributes::AppAttributes; -use crate::attributes::general_attributes::GeneralAttributes; +use crate::attributes::system_attributes::SystemAttributes; use crate::board_settings::BoardSettings; use crate::connection::TockloaderConnection; use crate::errors::TockloaderError; -use crate::tabs::tab::Tab; -use crate::{CommandEraseApps, CommandInfo, CommandInstall, CommandList}; +use crate::IOCommands; #[async_trait] -impl CommandList for TockloaderConnection { - async fn list( - &mut self, - settings: &BoardSettings, - ) -> Result, TockloaderError> { +impl IOCommands for TockloaderConnection { + async fn read(&mut self, address: u64, size: usize) -> Result, TockloaderError> { match self { - TockloaderConnection::ProbeRS(conn) => conn.list(settings).await, - TockloaderConnection::Serial(conn) => conn.list(settings).await, + TockloaderConnection::ProbeRS(conn) => conn.read(address, size).await, + TockloaderConnection::Serial(conn) => conn.read(address, size).await, } } -} -#[async_trait] -impl CommandInfo for TockloaderConnection { - async fn info( - &mut self, - settings: &BoardSettings, - ) -> Result { + async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError> { match self { - TockloaderConnection::ProbeRS(conn) => conn.info(settings).await, - TockloaderConnection::Serial(conn) => conn.info(settings).await, + TockloaderConnection::ProbeRS(conn) => conn.write(address, pkt).await, + TockloaderConnection::Serial(conn) => conn.write(address, pkt).await, } } -} -#[async_trait] -impl CommandInstall for TockloaderConnection { - async fn install_app( + async fn list_apps( &mut self, settings: &BoardSettings, - tab_file: Tab, - ) -> Result<(), TockloaderError> { + ) -> Result, TockloaderError> { match self { - TockloaderConnection::ProbeRS(conn) => conn.install_app(settings, tab_file).await, - TockloaderConnection::Serial(conn) => conn.install_app(settings, tab_file).await, + TockloaderConnection::ProbeRS(conn) => conn.list_apps(settings).await, + TockloaderConnection::Serial(conn) => conn.list_apps(settings).await, } } -} -#[async_trait] -impl CommandEraseApps for TockloaderConnection { - async fn erase_apps(&mut self, settings: &BoardSettings) -> Result<(), TockloaderError> { + async fn read_system_attributes(&mut self) -> Result { match self { - TockloaderConnection::ProbeRS(conn) => conn.erase_apps(settings).await, - TockloaderConnection::Serial(conn) => conn.erase_apps(settings).await, + TockloaderConnection::ProbeRS(conn) => conn.read_system_attributes().await, + TockloaderConnection::Serial(conn) => conn.read_system_attributes().await, } } } diff --git a/tockloader-lib/src/command_impl/info.rs b/tockloader-lib/src/command_impl/info.rs new file mode 100644 index 00000000..1efbc97d --- /dev/null +++ b/tockloader-lib/src/command_impl/info.rs @@ -0,0 +1,19 @@ +use async_trait::async_trait; + +use crate::attributes::general_attributes::GeneralAttributes; +use crate::board_settings::BoardSettings; +use crate::connection::TockloaderConnection; +use crate::errors::TockloaderError; +use crate::{CommandInfo, IOCommands}; + +#[async_trait] +impl CommandInfo for TockloaderConnection { + async fn info( + &mut self, + settings: &BoardSettings, + ) -> Result { + let installed_apps = self.list_apps(settings).await.unwrap(); + let system_atributes = self.read_system_attributes().await.unwrap(); + Ok(GeneralAttributes::new(system_atributes, installed_apps)) + } +} diff --git a/tockloader-lib/src/command_impl/install.rs b/tockloader-lib/src/command_impl/install.rs new file mode 100644 index 00000000..33f046b9 --- /dev/null +++ b/tockloader-lib/src/command_impl/install.rs @@ -0,0 +1,53 @@ +use async_trait::async_trait; + +use crate::attributes::app_attributes::AppAttributes; +use crate::board_settings::BoardSettings; +use crate::command_impl::reshuffle_apps::{create_pkt, reconstruct_app, reshuffle_apps}; +use crate::connection::TockloaderConnection; +use crate::errors::TockloaderError; +use crate::tabs::tab::Tab; +use crate::{CommandInstall, CommandList, IOCommands}; + +#[async_trait] +impl CommandInstall for TockloaderConnection { + async fn install_app( + &mut self, + settings: &BoardSettings, + tab: Tab, + ) -> Result<(), TockloaderError> { + // get the already installed apps + let mut installed_apps: Vec = self.list(settings).await.unwrap(); + + // reconstruct the new app + if let Some(mut app) = reconstruct_app(Some(&tab), settings) { + app.index = installed_apps.len() as u8; + installed_apps.push(app.clone()); + } + + // obtain the binaries in a vector + let mut app_binaries: Vec> = Vec::new(); + + for app in installed_apps.iter() { + match app.installed { + true => { + app_binaries.push(app.clone().read(self).await.unwrap()); + } + false => { + // TODO(adi): change this when TBF Filtering will get merged + app_binaries.push( + tab.extract_binary(settings.arch.as_ref().unwrap().as_str()) + .unwrap(), + ); + } + } + } + let configuration = reshuffle_apps(settings, installed_apps).unwrap(); + + // create the pkt, this contains all the binaries in a vec + let pkt = create_pkt(configuration, app_binaries); + + // write the pkt + let _ = self.write(settings.start_address, pkt).await; + Ok(()) + } +} diff --git a/tockloader-lib/src/command_impl/list.rs b/tockloader-lib/src/command_impl/list.rs new file mode 100644 index 00000000..c1b0abff --- /dev/null +++ b/tockloader-lib/src/command_impl/list.rs @@ -0,0 +1,17 @@ +use async_trait::async_trait; + +use crate::attributes::app_attributes::AppAttributes; +use crate::board_settings::BoardSettings; +use crate::connection::TockloaderConnection; +use crate::errors::TockloaderError; +use crate::{CommandList, IOCommands}; + +#[async_trait] +impl CommandList for TockloaderConnection { + async fn list( + &mut self, + settings: &BoardSettings, + ) -> Result, TockloaderError> { + self.list_apps(settings).await + } +} diff --git a/tockloader-lib/src/command_impl/mod.rs b/tockloader-lib/src/command_impl/mod.rs index 46aa06ea..22d488e1 100644 --- a/tockloader-lib/src/command_impl/mod.rs +++ b/tockloader-lib/src/command_impl/mod.rs @@ -1,3 +1,8 @@ +pub mod erase_apps; pub mod generalized; +pub mod info; +pub mod install; +pub mod list; pub mod probers; +pub mod reshuffle_apps; pub mod serial; diff --git a/tockloader-lib/src/command_impl/probers/erase_apps.rs b/tockloader-lib/src/command_impl/probers/erase_apps.rs deleted file mode 100644 index 24e90f2f..00000000 --- a/tockloader-lib/src/command_impl/probers/erase_apps.rs +++ /dev/null @@ -1,32 +0,0 @@ -use async_trait::async_trait; -use probe_rs::flashing::DownloadOptions; - -use crate::board_settings::BoardSettings; -use crate::connection::{Connection, ProbeRSConnection}; -use crate::errors::{InternalError, TockloaderError}; -use crate::CommandEraseApps; - -#[async_trait] -impl CommandEraseApps for ProbeRSConnection { - async fn erase_apps(&mut self, settings: &BoardSettings) -> Result<(), TockloaderError> { - if !self.is_open() { - return Err(InternalError::ConnectionNotOpen.into()); - } - let session = self.session.as_mut().expect("Board must be open"); - - let mut loader = session.target().flash_loader(); - - let address = settings.start_address; - // A single 0x0 byte is enough to invalidate the tbf header and make it all programs - // unreadable to tockloader. This does mean app information will still exist on the board, - // but they will be overwritten when the space is needed. - loader.add_data((address as u32).into(), &[0x0])?; - - let mut options = DownloadOptions::default(); - options.keep_unwritten_bytes = true; - - // Finally, the data can be programmed - loader.commit(session, options)?; - Ok(()) - } -} diff --git a/tockloader-lib/src/command_impl/probers/info.rs b/tockloader-lib/src/command_impl/probers/info.rs deleted file mode 100644 index 113f2360..00000000 --- a/tockloader-lib/src/command_impl/probers/info.rs +++ /dev/null @@ -1,31 +0,0 @@ -use async_trait::async_trait; - -use crate::attributes::app_attributes::AppAttributes; -use crate::attributes::general_attributes::GeneralAttributes; -use crate::attributes::system_attributes::SystemAttributes; -use crate::board_settings::BoardSettings; -use crate::connection::{Connection, ProbeRSConnection}; -use crate::errors::{InternalError, TockloaderError}; -use crate::CommandInfo; - -#[async_trait] -impl CommandInfo for ProbeRSConnection { - async fn info( - &mut self, - settings: &BoardSettings, - ) -> Result { - if !self.is_open() { - return Err(InternalError::ConnectionNotOpen.into()); - } - let session = self.session.as_mut().expect("Board must be open"); - - let mut core = session.core(self.target_info.core)?; - - // TODO(george-cosma): extract these informations without bootloader - let system_attributes = SystemAttributes::read_system_attributes_probe(&mut core)?; - let app_attributes = - AppAttributes::read_apps_data_probe(&mut core, settings.start_address)?; - - Ok(GeneralAttributes::new(system_attributes, app_attributes)) - } -} diff --git a/tockloader-lib/src/command_impl/probers/install.rs b/tockloader-lib/src/command_impl/probers/install.rs deleted file mode 100644 index af84572f..00000000 --- a/tockloader-lib/src/command_impl/probers/install.rs +++ /dev/null @@ -1,161 +0,0 @@ -use async_trait::async_trait; -use probe_rs::flashing::DownloadOptions; -use probe_rs::MemoryInterface; -use tbf_parser::parse::parse_tbf_header_lengths; - -use crate::board_settings::BoardSettings; -use crate::connection::{Connection, ProbeRSConnection}; -use crate::errors::{InternalError, TockloaderError}; -use crate::tabs::tab::Tab; -use crate::CommandInstall; - -#[async_trait] -impl CommandInstall for ProbeRSConnection { - async fn install_app( - &mut self, - settings: &BoardSettings, - tab_file: Tab, - ) -> Result<(), TockloaderError> { - if !self.is_open() { - return Err(InternalError::ConnectionNotOpen.into()); - } - let session = self.session.as_mut().expect("Board must be open"); - - let mut core = session.core(self.target_info.core)?; - - // TODO(george-cosma): extract these informations without bootloader - // TODO(george-cosma): extract board name and kernel version to verify app compatability - - let mut address = settings.start_address; - - // TODO(george-cosma): double-check/rework this - - // Read a block of 200 8-bit words// Loop to check if there are another apps installed - loop { - let mut buff = vec![0u8; 200]; - core.read(address, &mut buff)?; - - let (_ver, _header_len, whole_len) = match parse_tbf_header_lengths( - &buff[0..8] - .try_into() - .expect("Buffer length must be at least 8 bytes long."), - ) { - Ok((ver, header_len, whole_len)) if header_len != 0 => (ver, header_len, whole_len), - _ => break, // No more apps - }; - address += whole_len as u64; - } - - // TODO(george-cosma): extract arch(?) - // TODO(george-cosma): THIS IS NOT A TOCK ERROR, this is an error due to invalid board settings. - let arch = settings - .arch - .as_ref() - .ok_or(InternalError::MisconfiguredBoardSettings( - "architechture".to_owned(), - ))?; - - let mut binary = tab_file.extract_binary(arch)?; - let size = binary.len() as u64; - - // Make sure the app is aligned to a multiple of its size - let multiple = address / size; - - let (new_address, _gap_size) = if multiple * size != address { - let new_address = ((address + size) / size) * size; - let gap_size = new_address - address; - (new_address, gap_size) - } else { - (address, 0) - }; - - // TODO(george-cosma): This point MIGHT mark a good point to split - // this function (for probe-rs). - - // At this point we no longer need to hold the probe-rs connection - // to the core, as the flashing is done without it. - drop(core); - - // Make sure the binary is a multiple of the page size by padding 0xFFs - - // TODO(george-cosma): check if the page-size differs + support - // multiple types of page sizes. Possibly make page size a board - // setting. - let page_size = 512; - let needs_padding = binary.len() % page_size != 0; - - if needs_padding { - let remaining = page_size - (binary.len() % page_size); - dbg!(remaining); - for _i in 0..remaining { - binary.push(0xFF); - } - } - - // Get indices of pages that have valid data to write - let mut valid_pages: Vec = Vec::new(); - for i in 0..(size as usize / page_size) { - for b in binary[(i * page_size)..((i + 1) * page_size)] - .iter() - .copied() - { - if b != 0 { - valid_pages.push(i.try_into().unwrap()); - break; - } - } - } - - // If there are no pages valid, all pages would have been removed, - // so we write them all - if valid_pages.is_empty() { - for i in 0..(size as usize / page_size) { - valid_pages.push(i.try_into().unwrap()); - } - } - - // Include a blank page (if exists) after the end of a valid page. - // There might be a usable 0 on the next page - let mut ending_pages: Vec = Vec::new(); - for &i in &valid_pages { - let mut iter = valid_pages.iter(); - if !iter.any(|&x| x == (i + 1)) && (i + 1) < (size as usize / page_size) as u8 { - ending_pages.push(i + 1); - } - } - - for i in ending_pages { - valid_pages.push(i); - } - - for i in valid_pages { - println!("Writing page number {i}"); - // Create the packet that we send to the bootloader. First four - // bytes are the address of the page - let mut pkt = Vec::new(); - - // Then the bytes that go into the page - for b in binary[(i as usize * page_size)..((i + 1) as usize * page_size)] - .iter() - .copied() - { - pkt.push(b); - } - let mut loader = session.target().flash_loader(); - - loader.add_data( - (new_address as u32 + (i as usize * page_size) as u32).into(), - &pkt, - )?; - - let mut options = DownloadOptions::default(); - options.keep_unwritten_bytes = true; - - // Finally, the data can be programmed - // TODO(george-cosma): Can we move this outside the loop? Commit once? - loader.commit(session, options)?; - } - - Ok(()) - } -} diff --git a/tockloader-lib/src/command_impl/probers/io.rs b/tockloader-lib/src/command_impl/probers/io.rs new file mode 100644 index 00000000..fc2f4271 --- /dev/null +++ b/tockloader-lib/src/command_impl/probers/io.rs @@ -0,0 +1,67 @@ +use async_trait::async_trait; +use probe_rs::{flashing::DownloadOptions, MemoryInterface}; + +use crate::{ + attributes::{app_attributes::AppAttributes, system_attributes::SystemAttributes}, + board_settings::BoardSettings, + connection::{Connection, ProbeRSConnection}, + errors::{InternalError, TockloaderError}, + IOCommands, +}; + +#[async_trait] +impl IOCommands for ProbeRSConnection { + async fn read(&mut self, address: u64, size: usize) -> Result, TockloaderError> { + if !self.is_open() { + return Err(InternalError::ConnectionNotOpen.into()); + } + let session = self.session.as_mut().expect("Board must be open"); + + let mut core = session.core(self.target_info.core)?; + let mut appdata = vec![0u8; size]; + core.read(address, &mut appdata)?; + Ok(appdata) + } + + async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError> { + if !self.is_open() { + return Err(InternalError::ConnectionNotOpen.into()); + } + let session = self.session.as_mut().expect("Board must be open"); + let mut loader = session.target().flash_loader(); + + loader.add_data(address, &pkt)?; + + let mut options = DownloadOptions::default(); + options.keep_unwritten_bytes = true; + + loader.commit(session, options)?; + Ok(()) + } + + async fn list_apps( + &mut self, + settings: &BoardSettings, + ) -> Result, TockloaderError> { + if !self.is_open() { + return Err(InternalError::ConnectionNotOpen.into()); + } + let session = self.session.as_mut().expect("Board must be open"); + let mut core = session.core(self.target_info.core)?; + + AppAttributes::read_apps_data_probe(&mut core, settings.start_address) + } + + async fn read_system_attributes(&mut self) -> Result { + if !self.is_open() { + return Err(InternalError::ConnectionNotOpen.into()); + } + let session = self.session.as_mut().expect("Board must be open"); + + let mut core = session.core(self.target_info.core)?; + + // TODO(george-cosma): extract these informations without bootloader + let system_attributes = SystemAttributes::read_system_attributes_probe(&mut core)?; + Ok(system_attributes) + } +} diff --git a/tockloader-lib/src/command_impl/probers/list.rs b/tockloader-lib/src/command_impl/probers/list.rs deleted file mode 100644 index 11eab7fb..00000000 --- a/tockloader-lib/src/command_impl/probers/list.rs +++ /dev/null @@ -1,24 +0,0 @@ -use async_trait::async_trait; - -use crate::attributes::app_attributes::AppAttributes; -use crate::board_settings::BoardSettings; -use crate::connection::{Connection, ProbeRSConnection}; -use crate::errors::{InternalError, TockloaderError}; -use crate::CommandList; - -#[async_trait] -impl CommandList for ProbeRSConnection { - async fn list( - &mut self, - settings: &BoardSettings, - ) -> Result, TockloaderError> { - if !self.is_open() { - return Err(InternalError::ConnectionNotOpen.into()); - } - let session = self.session.as_mut().expect("Board must be open"); - - let mut core = session.core(self.target_info.core)?; - - AppAttributes::read_apps_data_probe(&mut core, settings.start_address) - } -} diff --git a/tockloader-lib/src/command_impl/probers/mod.rs b/tockloader-lib/src/command_impl/probers/mod.rs index dda4278b..af514a1e 100644 --- a/tockloader-lib/src/command_impl/probers/mod.rs +++ b/tockloader-lib/src/command_impl/probers/mod.rs @@ -1,4 +1 @@ -pub mod erase_apps; -pub mod info; -pub mod install; -pub mod list; +pub mod io; diff --git a/tockloader-lib/src/command_impl/reshuffle_apps.rs b/tockloader-lib/src/command_impl/reshuffle_apps.rs new file mode 100644 index 00000000..5ea3eb10 --- /dev/null +++ b/tockloader-lib/src/command_impl/reshuffle_apps.rs @@ -0,0 +1,291 @@ +use itertools::Itertools; +use tbf_parser::parse::{parse_tbf_footer, parse_tbf_header, parse_tbf_header_lengths}; + +use crate::attributes::app_attributes::{AppAttributes, TbfFooter}; +use crate::board_settings::BoardSettings; +use crate::errors::{InternalError, TockloaderError}; +use crate::tabs::tab::Tab; + +const ALIGNMENT: u64 = 1024; +pub const PAGE_SIZE: u32 = 512; + +pub fn reshuffle_apps( + settings: &BoardSettings, + installed_apps: Vec, +) -> Result, TockloaderError> { + // separate the apps + let mut rust_apps: Vec = Vec::new(); + let mut c_apps: Vec = Vec::new(); + for app in installed_apps.iter() { + match app.tbf_header.get_fixed_address_flash() { + Some(_) => { + rust_apps.push(app.clone()); + } + None => { + c_apps.push(app.clone()); + } + } + } + + // this is necessary. If a rust app is already installed, for example: at 0x48000 + // and we want to install another one at 0x40000, reorder them first + rust_apps.sort_by_key(|app| app.address); + + // make permutations only for the c apps, as their order can be changed + let mut permutations = (0..c_apps.len()).permutations(c_apps.len()); + + let mut min_padding = usize::MAX; + let mut saved_configuration: Vec = Vec::new(); + + for _ in 0..100_000 { + // use just 100k permutations, or else we'll be here for a while + match permutations.next() { + Some(order) => { + let mut total_padding: usize = 0; + let mut permutation_index: usize = 0; + let mut rust_index: usize = 0; + let mut reordered_apps: Vec = Vec::new(); + loop { + let insert_c: bool; // every iteration will insert an app, or break if there are none left + + // start either where the last app ends, or at start address if there are no apps + let address = match reordered_apps.last() { + Some(app) => app.address + app.size as u64, + None => settings.start_address, + }; + + match order.get(permutation_index) { + Some(_) => { + // we have a C app + match rust_apps.get(rust_index) { + Some(_) => { + // we also have a rust app, insert only if it fits + insert_c = c_apps[order[permutation_index]].size + <= (rust_apps[rust_index].address - address) as u32; + } + None => { + // we have only a C app, insert it accordingly + insert_c = true; + } + } + } + None => { + // we don't have a c app + match rust_apps.get(rust_index) { + Some(_) => { + // we have a rust app, insert it + insert_c = false; + } + None => { + // we don't have any app, break? + break; + } + } + } + } + + let mut start_address: u64; + if reordered_apps.is_empty() { + // is padding needed when starting from settings.start_address? + start_address = settings.start_address; + } else { + // start the padding where last app ends + let last_app = reordered_apps.last().unwrap(); + start_address = last_app.address + last_app.size as u64; + } + let needed_padding: u32 = if insert_c { + if !start_address.is_multiple_of(PAGE_SIZE as u64) { + PAGE_SIZE - start_address as u32 % PAGE_SIZE // c app needs to be inserted at a multiple of page_size + } else { + 0 + } + } else { + if rust_apps[rust_index].address < start_address { + // the program wants to insert a rust app where another rust app already exists + panic!("Can't insert the rust app, space is already occupied by another rust app"); + // we can't change the start address, so panic + } + (rust_apps[rust_index].address - start_address) as u32 + // rust app needs to be inserted at a fixed address, pad until there + }; + if needed_padding > 0 { + // insert a padding + total_padding += needed_padding as usize; + reordered_apps.push(installed_apps[0].clone()); + reordered_apps.last_mut().unwrap().address = start_address; + reordered_apps.last_mut().unwrap().size = needed_padding; + reordered_apps.last_mut().unwrap().is_padding = true; + start_address += needed_padding as u64; + } + if insert_c { + // insert the c app, also change its address + reordered_apps.push(c_apps[order[permutation_index]].clone()); + reordered_apps.last_mut().unwrap().address = start_address; + permutation_index += 1; + } else { + // insert the rust app, don't change its address because it is fixed + reordered_apps.push(rust_apps[rust_index].clone()); + rust_index += 1; + } + } + + // find the configuration that uses the minimum padding + if total_padding < min_padding { + min_padding = total_padding; + saved_configuration = reordered_apps.clone(); + } + } + None => break, + } + } + + log::debug!("min padding is {min_padding}"); + // panic!(); + let mut index = 0; + for item in saved_configuration.iter() { + if item.is_padding { + log::debug!( + "-----PADDING APP------, start:{:#x}, size {}, end {:#x}", + item.address, + item.size, + item.address + item.size as u64 + ); + } else { + log::debug!( + "{}. {}, start address {:#x}, size {}, rustapp {}, end address {:#x}", + index, + item.tbf_header.get_package_name().unwrap_or(""), + item.address, + item.size, + match item.tbf_header.get_fixed_address_flash() { + Some(_) => { + true + } + None => { + false + } + }, + item.address + item.size as u64, + ); + index += 1; + } + } + Ok(saved_configuration) +} + +// this function takes a tab and turns it into an AppAttributes instance +pub fn reconstruct_app(tab: Option<&Tab>, settings: &BoardSettings) -> Option { + if let Some(tab) = tab { + let arch = settings + .arch + .as_ref() + .ok_or(InternalError::MisconfiguredBoardSettings( + "architechture".to_owned(), + )) + .unwrap(); + + // extract the binary + let binary = tab.extract_binary(arch).expect("invalid arch"); + + // extract relevant data from the header + let (tbf_version, header_len, total_size) = match parse_tbf_header_lengths( + &binary[0..8] + .try_into() + .expect("Buffer length must be at least 8 bytes long."), + ) { + Ok((tbf_version, header_len, total_size)) if header_len != 0 => { + (tbf_version, header_len, total_size) + } + _ => return None, + }; + + // turn the buffer slice into a TbfHeader instance + let header = + parse_tbf_header(&binary[0..header_len as usize], tbf_version).expect("invalid header"); + let binary_end_offset = header.get_binary_end(); + + // obtain the footers + let mut footers: Vec = vec![]; + let mut footer_offset = binary_end_offset; + let mut footer_number = 0; + + while footer_offset < total_size { + let mut appfooter: Vec = binary[footer_offset as usize..].to_vec(); + let byte1 = *appfooter.get(2).unwrap(); + let byte2 = *appfooter.get(3).unwrap(); + let expected_size = u16::from_le_bytes([byte1, byte2]) + 4; + // insert or remove until we have expected size? + // insert + while appfooter.len() < expected_size as usize { + appfooter.push(0x0u8); + } + // delete + while appfooter.len() > expected_size as usize { + appfooter.pop(); + } + // (these might be useless) + + let footer_info = parse_tbf_footer(&appfooter).expect("Invalid footer!"); + footers.insert(footer_number, TbfFooter::new(footer_info.0, footer_info.1)); + footer_number += 1; + footer_offset += footer_info.1 + 4; + } + + // create an AppAttribute using the data we obtained + return Some(AppAttributes::new( + if let Some(addr) = header.get_fixed_address_flash() { + if addr < settings.start_address as u32 { + // this rust app should not be here + panic!("This rust app starts at {addr:#x}, while the board's start_address is {:#x}", settings.start_address) + } + // turns out that fixed address is a loosely-used term, address has to be aligned down to a multiple of 1024 bytes + align_down(addr as u64) + } else { + settings.start_address + }, + total_size, + 0, + header, + footers, + false, + )); + } + None +} + +// this return only the binary for a padding +fn create_padding(size: u32) -> Vec { + let mut buf: Vec = Vec::new(); + buf.extend_from_slice(&u16::to_le_bytes(2u16)); // tbf version 2 + buf.extend_from_slice(&u16::to_le_bytes(16u16)); // header size is 16 + buf.extend_from_slice(&u32::to_le_bytes(size)); // total_size is size + let mut checksum = 0; + for chunk in buf.chunks_exact(4) { + let word = u32::from_le_bytes(chunk.try_into().unwrap()); + checksum ^= word; + } + buf.extend_from_slice(&u32::to_le_bytes(checksum)); + while buf.len() < size as usize { + buf.push(0x0u8); + } + buf +} + +// this function takes a rust app's fixed address and aligns it down to ALIGNMENT (1024 currently) +fn align_down(address: u64) -> u64 { + address - address % ALIGNMENT +} + +pub fn create_pkt(configuration: Vec, mut app_binaries: Vec>) -> Vec { + let mut pkt: Vec = Vec::new(); + for item in configuration.iter() { + if item.is_padding { + // write padding binary + let mut buf = create_padding(item.size); + pkt.append(&mut buf); + } else { + pkt.append(&mut app_binaries[item.index as usize]); + } + } + pkt +} diff --git a/tockloader-lib/src/command_impl/serial/info.rs b/tockloader-lib/src/command_impl/serial/info.rs deleted file mode 100644 index 57770ccf..00000000 --- a/tockloader-lib/src/command_impl/serial/info.rs +++ /dev/null @@ -1,31 +0,0 @@ -use async_trait::async_trait; - -use crate::attributes::app_attributes::AppAttributes; -use crate::attributes::general_attributes::GeneralAttributes; -use crate::attributes::system_attributes::SystemAttributes; -use crate::board_settings::BoardSettings; -use crate::bootloader_serial::ping_bootloader_and_wait_for_response; -use crate::connection::{Connection, SerialConnection}; -use crate::errors::{InternalError, TockloaderError}; -use crate::CommandInfo; - -#[async_trait] -impl CommandInfo for SerialConnection { - async fn info( - &mut self, - settings: &BoardSettings, - ) -> Result { - if !self.is_open() { - return Err(InternalError::ConnectionNotOpen.into()); - } - let stream = self.stream.as_mut().expect("Board must be open"); - - ping_bootloader_and_wait_for_response(stream).await?; - - let system_attributes = SystemAttributes::read_system_attributes_serial(stream).await?; - let app_attributes = - AppAttributes::read_apps_data_serial(stream, settings.start_address).await?; - - Ok(GeneralAttributes::new(system_attributes, app_attributes)) - } -} diff --git a/tockloader-lib/src/command_impl/serial/install.rs b/tockloader-lib/src/command_impl/serial/install.rs deleted file mode 100644 index c9ba88c7..00000000 --- a/tockloader-lib/src/command_impl/serial/install.rs +++ /dev/null @@ -1,239 +0,0 @@ -use async_trait::async_trait; - -use crate::board_settings::BoardSettings; -use crate::connection::SerialConnection; -use crate::errors::TockloaderError; -use crate::tabs::tab::Tab; -use crate::CommandInstall; - -#[async_trait] -impl CommandInstall for SerialConnection { - async fn install_app( - &mut self, - _settings: &BoardSettings, - _tab_file: Tab, - ) -> Result<(), TockloaderError> { - todo!() - } -} - -// pub async fn install_app( -// choice: Connection, -// core_index: Option<&usize>, -// tab_file: Tab, -// ) -> Result<(), TockloaderError> { -// match choice { -// Connection::ProbeRS(mut session) => { -// // *snip* -// } -// Connection::Serial(mut port) => { -// let response = ping_bootloader_and_wait_for_response(&mut port).await?; -// -// if response as u8 != Response::Pong as u8 { -// tokio::time::sleep(Duration::from_millis(100)).await; -// let _ = ping_bootloader_and_wait_for_response(&mut port).await?; -// } - -// let system_attributes = -// SystemAttributes::read_system_attributes_serial(&mut port).await?; - -// let board = system_attributes -// .board -// .ok_or("No board name found.".to_owned()); -// let kernel_version = system_attributes -// .kernel_version -// .ok_or("No kernel version found.".to_owned()); - -// match board { -// Ok(board) => { -// // Verify if the specified app is compatible with board -// // TODO(Micu Ana): Replace the print with log messages -// if tab_file.is_compatible_with_board(&board) { -// println!("Specified tab is compatible with board."); -// } else { -// panic!("Specified tab is not compatible with board."); -// } -// } -// Err(e) => { -// return Err(TockloaderError::MisconfiguredBoard(e)); -// } -// } - -// match kernel_version { -// Ok(kernel_version) => { -// // Verify if the specified app is compatible with kernel version -// // TODO(Micu Ana): Replace the prints with log messages -// if tab_file.is_compatible_with_kernel_verison(kernel_version as u32) { -// println!("Specified tab is compatible with your kernel version."); -// } else { -// println!("Specified tab is not compatible with your kernel version."); -// } -// } -// Err(e) => { -// return Err(TockloaderError::MisconfiguredBoard(e)); -// } -// } - -// let mut address = -// system_attributes -// .appaddr -// .ok_or(TockloaderError::MisconfiguredBoard( -// "No start address found.".to_owned(), -// ))?; -// loop { -// // Read a block of 200 8-bit words -// let mut pkt = (address as u32).to_le_bytes().to_vec(); -// let length = (200_u16).to_le_bytes().to_vec(); -// for i in length { -// pkt.push(i); -// } - -// let (_, message) = issue_command( -// &mut port, -// Command::ReadRange, -// pkt, -// true, -// 200, -// Response::ReadRange, -// ) -// .await?; - -// let (_ver, _header_len, whole_len) = match parse_tbf_header_lengths( -// &message[0..8] -// .try_into() -// .expect("Buffer length must be at least 8 bytes long."), -// ) { -// Ok((ver, header_len, whole_len)) if header_len != 0 => { -// (ver, header_len, whole_len) -// } -// _ => break, // No more apps -// }; - -// address += whole_len as u64; -// } - -// let arch = system_attributes -// .arch -// .ok_or("No architecture found.".to_owned()); - -// match arch { -// Ok(arch) => { -// let binary = tab_file.extract_binary(&arch.clone()); - -// match binary { -// Ok(mut binary) => { -// let size = binary.len() as u64; - -// let multiple = address / size; - -// let (mut new_address, _gap_size) = if multiple * size != address { -// let new_address = ((address + size) / size) * size; -// let gap_size = new_address - address; -// (new_address, gap_size) -// } else { -// (address, 0) -// }; - -// // Make sure the binary is a multiple of the page size by padding 0xFFs -// // TODO(Micu Ana): check if the page-size differs -// let page_size = 512; -// let needs_padding = binary.len() % page_size != 0; - -// if needs_padding { -// let remaining = page_size - (binary.len() % page_size); -// for _i in 0..remaining { -// binary.push(0xFF); -// } -// } - -// let binary_len = binary.len(); - -// // Get indices of pages that have valid data to write -// let mut valid_pages: Vec = Vec::new(); -// for i in 0..(binary_len / page_size) { -// for b in binary[(i * page_size)..((i + 1) * page_size)] -// .iter() -// .copied() -// { -// if b != 0 { -// valid_pages.push(i as u8); -// break; -// } -// } -// } - -// // If there are no pages valid, all pages would have been removed, so we write them all -// if valid_pages.is_empty() { -// for i in 0..(binary_len / page_size) { -// valid_pages.push(i as u8); -// } -// } - -// // Include a blank page (if exists) after the end of a valid page. There might be a usable 0 on the next page -// let mut ending_pages: Vec = Vec::new(); -// for &i in &valid_pages { -// let mut iter = valid_pages.iter(); -// if !iter.any(|&x| x == (i + 1)) -// && (i + 1) < (binary_len / page_size) as u8 -// { -// ending_pages.push(i + 1); -// } -// } - -// for i in ending_pages { -// valid_pages.push(i); -// } - -// for i in valid_pages { -// // Create the packet that we send to the bootloader -// // First four bytes are the address of the page -// let mut pkt = (new_address as u32 -// + (i as usize * page_size) as u32) -// .to_le_bytes() -// .to_vec(); -// // Then the bytes that go into the page -// for b in binary -// [(i as usize * page_size)..((i + 1) as usize * page_size)] -// .iter() -// .copied() -// { -// pkt.push(b); -// } - -// // Write to bootloader -// let (_, _) = issue_command( -// &mut port, -// Command::WritePage, -// pkt, -// true, -// 0, -// Response::OK, -// ) -// .await?; -// } - -// new_address += binary.len() as u64; - -// let pkt = (new_address as u32).to_le_bytes().to_vec(); - -// let _ = issue_command( -// &mut port, -// Command::ErasePage, -// pkt, -// true, -// 0, -// Response::OK, -// ) -// .await?; -// } -// Err(e) => { -// return Err(e); -// } -// } -// Ok(()) -// } -// Err(e) => Err(TockloaderError::MisconfiguredBoard(e)), -// } -// } -// } -// } diff --git a/tockloader-lib/src/command_impl/serial/io.rs b/tockloader-lib/src/command_impl/serial/io.rs new file mode 100644 index 00000000..948f8e23 --- /dev/null +++ b/tockloader-lib/src/command_impl/serial/io.rs @@ -0,0 +1,96 @@ +use async_trait::async_trait; + +use crate::{ + attributes::{app_attributes::AppAttributes, system_attributes::SystemAttributes}, + board_settings::BoardSettings, + bootloader_serial::{issue_command, ping_bootloader_and_wait_for_response, Command, Response}, + command_impl::reshuffle_apps::PAGE_SIZE, + connection::{Connection, SerialConnection}, + errors::{InternalError, TockloaderError}, + IOCommands, +}; + +#[async_trait] +impl IOCommands for SerialConnection { + async fn read(&mut self, address: u64, size: usize) -> Result, TockloaderError> { + let mut pkt = (address as u32).to_le_bytes().to_vec(); + let length = (size as u16).to_le_bytes().to_vec(); + for i in length { + pkt.push(i); + } + let stream = self.stream.as_mut().expect("Board must be open"); + + let (_, appdata) = issue_command( + stream, + Command::ReadRange, + pkt, + true, + size, + Response::ReadRange, + ) + .await?; + + // this might be very specific, but when I was testing, i got 8191 bytes instead of 8192 + // this is because of the consecutive ESCAPE_CHAR rule inside issue_command function + + if appdata.len() < size { + panic!("Appdata was not read correctly?? We should not reach here"); + } + Ok(appdata) + } + + async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError> { + let stream = self.stream.as_mut().expect("Board must be open"); + let mut binary = pkt.clone(); + while !binary.len().is_multiple_of(PAGE_SIZE as usize) { + binary.push(0u8); + } + + let mut page_number = 0; + while (page_number + 1) * PAGE_SIZE <= binary.len() as u32 { + let mut pkt = (address as u32 + page_number * PAGE_SIZE) + .to_le_bytes() + .to_vec(); + pkt.append( + &mut binary + [(page_number * PAGE_SIZE) as usize..((page_number + 1) * PAGE_SIZE) as usize] + .to_vec(), + ); + let _ = issue_command(stream, Command::WritePage, pkt, true, 0, Response::OK).await?; + page_number += 1; + } + + let pkt = (address as u32 + binary.len() as u32) + .to_le_bytes() + .to_vec(); + + let _ = issue_command(stream, Command::ErasePage, pkt, true, 0, Response::OK).await?; + Ok(()) + } + + async fn list_apps( + &mut self, + settings: &BoardSettings, + ) -> Result, TockloaderError> { + if !self.is_open() { + return Err(InternalError::ConnectionNotOpen.into()); + } + let stream = self.stream.as_mut().expect("Board must be open"); + + ping_bootloader_and_wait_for_response(stream).await?; + + AppAttributes::read_apps_data_serial(stream, settings.start_address).await + } + + async fn read_system_attributes(&mut self) -> Result { + if !self.is_open() { + return Err(InternalError::ConnectionNotOpen.into()); + } + let stream = self.stream.as_mut().expect("Board must be open"); + + ping_bootloader_and_wait_for_response(stream).await?; + + let system_attributes = SystemAttributes::read_system_attributes_serial(stream).await?; + Ok(system_attributes) + } +} diff --git a/tockloader-lib/src/command_impl/serial/list.rs b/tockloader-lib/src/command_impl/serial/list.rs deleted file mode 100644 index 268a9040..00000000 --- a/tockloader-lib/src/command_impl/serial/list.rs +++ /dev/null @@ -1,25 +0,0 @@ -use async_trait::async_trait; - -use crate::attributes::app_attributes::AppAttributes; -use crate::board_settings::BoardSettings; -use crate::bootloader_serial::ping_bootloader_and_wait_for_response; -use crate::connection::{Connection, SerialConnection}; -use crate::errors::{InternalError, TockloaderError}; -use crate::CommandList; - -#[async_trait] -impl CommandList for SerialConnection { - async fn list( - &mut self, - settings: &BoardSettings, - ) -> Result, TockloaderError> { - if !self.is_open() { - return Err(InternalError::ConnectionNotOpen.into()); - } - let stream = self.stream.as_mut().expect("Board must be open"); - - ping_bootloader_and_wait_for_response(stream).await?; - - AppAttributes::read_apps_data_serial(stream, settings.start_address).await - } -} diff --git a/tockloader-lib/src/command_impl/serial/mod.rs b/tockloader-lib/src/command_impl/serial/mod.rs index dda4278b..af514a1e 100644 --- a/tockloader-lib/src/command_impl/serial/mod.rs +++ b/tockloader-lib/src/command_impl/serial/mod.rs @@ -1,4 +1 @@ -pub mod erase_apps; -pub mod info; -pub mod install; -pub mod list; +pub mod io; diff --git a/tockloader-lib/src/lib.rs b/tockloader-lib/src/lib.rs index fdb79c5f..2b78148d 100644 --- a/tockloader-lib/src/lib.rs +++ b/tockloader-lib/src/lib.rs @@ -17,6 +17,7 @@ use tokio_serial::SerialPortInfo; use crate::attributes::app_attributes::AppAttributes; use crate::attributes::general_attributes::GeneralAttributes; +use crate::attributes::system_attributes::SystemAttributes; use crate::board_settings::BoardSettings; use crate::errors::*; use crate::tabs::tab::Tab; @@ -64,3 +65,17 @@ pub trait CommandInstall { pub trait CommandEraseApps { async fn erase_apps(&mut self, settings: &BoardSettings) -> Result<(), TockloaderError>; } + +#[async_trait] +pub trait IOCommands: Send { + async fn read(&mut self, address: u64, size: usize) -> Result, TockloaderError>; + + async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError>; + + async fn list_apps( + &mut self, + settings: &BoardSettings, + ) -> Result, TockloaderError>; + + async fn read_system_attributes(&mut self) -> Result; +} diff --git a/tockloader-lib/src/tabs/tab.rs b/tockloader-lib/src/tabs/tab.rs index fe69d54a..9a5f11a4 100644 --- a/tockloader-lib/src/tabs/tab.rs +++ b/tockloader-lib/src/tabs/tab.rs @@ -44,12 +44,16 @@ impl Tab { let mut data = Vec::new(); archive_file.read_to_end(&mut data).map_err(TabError::IO)?; + // log::info!("read filename {:?}", file_name); + + // log::info!("data? {:?}", data); tbf_files.push(TbfFile { filename: file_name.to_string(), data, }); } } + // panic!(); match metadata { Some(metadata) => Ok(Tab { @@ -75,9 +79,17 @@ impl Tab { } } + // maybe change the parameter into board settings? pub fn extract_binary(&self, arch: &str) -> Result, TockloaderError> { for file in &self.tbf_files { if file.filename.starts_with(arch) { + // make an inquire that shows only relevant configurations? + // flash >= start_addr + // ram >= ??? + // we need a ram parameter in board_settings + // if file.filename.starts_with("cortex-m4.0x00040000.0x20008000") { // here i set it manually for testing + // TODO(adi): this needs a better implementation for rust apps, the tbf is not selected correctly + // should we select the tab manually? with inquire? return Ok(file.data.clone()); } } From d9c0012550123febdbedd81de64256ee77c26b19 Mon Sep 17 00:00:00 2001 From: addrian-77 Date: Sat, 18 Oct 2025 15:44:00 +0300 Subject: [PATCH 2/8] refactor: separated IO Commands Signed-off-by: addrian-77 --- tbf-parser/src/parse.rs | 7 ++-- .../src/attributes/app_attributes.rs | 5 +-- tockloader-lib/src/command_impl/erase_apps.rs | 4 +-- .../src/command_impl/generalized.rs | 13 +++++--- tockloader-lib/src/command_impl/info.rs | 2 +- tockloader-lib/src/command_impl/install.rs | 4 +-- tockloader-lib/src/command_impl/list.rs | 2 +- tockloader-lib/src/command_impl/probers/io.rs | 10 +++--- .../src/command_impl/serial/erase_apps.rs | 25 --------------- tockloader-lib/src/command_impl/serial/io.rs | 32 +++++++++++-------- tockloader-lib/src/lib.rs | 7 ++-- tockloader-lib/src/tabs/tab.rs | 12 ------- 12 files changed, 49 insertions(+), 74 deletions(-) delete mode 100644 tockloader-lib/src/command_impl/serial/erase_apps.rs diff --git a/tbf-parser/src/parse.rs b/tbf-parser/src/parse.rs index 47c00429..3baec0ee 100644 --- a/tbf-parser/src/parse.rs +++ b/tbf-parser/src/parse.rs @@ -203,10 +203,9 @@ pub fn parse_tbf_header( } types::TbfHeaderTypes::TbfHeaderWriteableFlashRegions => { // Length must be a multiple of the size of a region definition. - if (tlv_header.length as usize) - .is_multiple_of(mem::size_of::< - types::TbfHeaderV2WriteableFlashRegion, - >()) + if tlv_header.length as usize + % mem::size_of::() + == 0 { // Calculate how many writeable flash regions // there are specified in this header. diff --git a/tockloader-lib/src/attributes/app_attributes.rs b/tockloader-lib/src/attributes/app_attributes.rs index 39458b2d..0189edb6 100644 --- a/tockloader-lib/src/attributes/app_attributes.rs +++ b/tockloader-lib/src/attributes/app_attributes.rs @@ -11,7 +11,7 @@ use tokio_serial::SerialStream; use crate::bootloader_serial::{issue_command, Command, Response}; use crate::errors::{TockError, TockloaderError}; -use crate::IOCommands; +use crate::IO; /// This structure contains all relevant information about a tock application. /// @@ -326,7 +326,8 @@ impl AppAttributes { Ok(apps_details) } - pub async fn read(&mut self, conn: &mut dyn IOCommands) -> Result, TockloaderError> { + /// This function reads the full binary of a given app + pub async fn read_binary(&mut self, conn: &mut dyn IO) -> Result, TockloaderError> { conn.read(self.address, self.size as usize).await } } diff --git a/tockloader-lib/src/command_impl/erase_apps.rs b/tockloader-lib/src/command_impl/erase_apps.rs index 25a79618..100926c5 100644 --- a/tockloader-lib/src/command_impl/erase_apps.rs +++ b/tockloader-lib/src/command_impl/erase_apps.rs @@ -3,11 +3,11 @@ use async_trait::async_trait; use crate::board_settings::BoardSettings; use crate::connection::TockloaderConnection; use crate::errors::TockloaderError; -use crate::{CommandEraseApps, IOCommands}; +use crate::{CommandEraseApps, IO}; #[async_trait] impl CommandEraseApps for TockloaderConnection { async fn erase_apps(&mut self, settings: &BoardSettings) -> Result<(), TockloaderError> { - self.write(settings.start_address, [0x0].to_vec()).await + self.write(settings.start_address, vec![0u8]).await } } diff --git a/tockloader-lib/src/command_impl/generalized.rs b/tockloader-lib/src/command_impl/generalized.rs index 22f8f74d..574cae80 100644 --- a/tockloader-lib/src/command_impl/generalized.rs +++ b/tockloader-lib/src/command_impl/generalized.rs @@ -5,10 +5,10 @@ use crate::attributes::system_attributes::SystemAttributes; use crate::board_settings::BoardSettings; use crate::connection::TockloaderConnection; use crate::errors::TockloaderError; -use crate::IOCommands; +use crate::{IOCommands, IO}; #[async_trait] -impl IOCommands for TockloaderConnection { +impl IO for TockloaderConnection { async fn read(&mut self, address: u64, size: usize) -> Result, TockloaderError> { match self { TockloaderConnection::ProbeRS(conn) => conn.read(address, size).await, @@ -22,14 +22,17 @@ impl IOCommands for TockloaderConnection { TockloaderConnection::Serial(conn) => conn.write(address, pkt).await, } } +} - async fn list_apps( +#[async_trait] +impl IOCommands for TockloaderConnection { + async fn read_installed_apps( &mut self, settings: &BoardSettings, ) -> Result, TockloaderError> { match self { - TockloaderConnection::ProbeRS(conn) => conn.list_apps(settings).await, - TockloaderConnection::Serial(conn) => conn.list_apps(settings).await, + TockloaderConnection::ProbeRS(conn) => conn.read_installed_apps(settings).await, + TockloaderConnection::Serial(conn) => conn.read_installed_apps(settings).await, } } diff --git a/tockloader-lib/src/command_impl/info.rs b/tockloader-lib/src/command_impl/info.rs index 1efbc97d..dc502219 100644 --- a/tockloader-lib/src/command_impl/info.rs +++ b/tockloader-lib/src/command_impl/info.rs @@ -12,7 +12,7 @@ impl CommandInfo for TockloaderConnection { &mut self, settings: &BoardSettings, ) -> Result { - let installed_apps = self.list_apps(settings).await.unwrap(); + let installed_apps = self.read_installed_apps(settings).await.unwrap(); let system_atributes = self.read_system_attributes().await.unwrap(); Ok(GeneralAttributes::new(system_atributes, installed_apps)) } diff --git a/tockloader-lib/src/command_impl/install.rs b/tockloader-lib/src/command_impl/install.rs index 33f046b9..70997ce8 100644 --- a/tockloader-lib/src/command_impl/install.rs +++ b/tockloader-lib/src/command_impl/install.rs @@ -6,7 +6,7 @@ use crate::command_impl::reshuffle_apps::{create_pkt, reconstruct_app, reshuffle use crate::connection::TockloaderConnection; use crate::errors::TockloaderError; use crate::tabs::tab::Tab; -use crate::{CommandInstall, CommandList, IOCommands}; +use crate::{CommandInstall, CommandList, IO}; #[async_trait] impl CommandInstall for TockloaderConnection { @@ -30,7 +30,7 @@ impl CommandInstall for TockloaderConnection { for app in installed_apps.iter() { match app.installed { true => { - app_binaries.push(app.clone().read(self).await.unwrap()); + app_binaries.push(app.clone().read_binary(self).await.unwrap()); } false => { // TODO(adi): change this when TBF Filtering will get merged diff --git a/tockloader-lib/src/command_impl/list.rs b/tockloader-lib/src/command_impl/list.rs index c1b0abff..05097d0b 100644 --- a/tockloader-lib/src/command_impl/list.rs +++ b/tockloader-lib/src/command_impl/list.rs @@ -12,6 +12,6 @@ impl CommandList for TockloaderConnection { &mut self, settings: &BoardSettings, ) -> Result, TockloaderError> { - self.list_apps(settings).await + self.read_installed_apps(settings).await } } diff --git a/tockloader-lib/src/command_impl/probers/io.rs b/tockloader-lib/src/command_impl/probers/io.rs index fc2f4271..0f99df28 100644 --- a/tockloader-lib/src/command_impl/probers/io.rs +++ b/tockloader-lib/src/command_impl/probers/io.rs @@ -6,11 +6,11 @@ use crate::{ board_settings::BoardSettings, connection::{Connection, ProbeRSConnection}, errors::{InternalError, TockloaderError}, - IOCommands, + IOCommands, IO, }; #[async_trait] -impl IOCommands for ProbeRSConnection { +impl IO for ProbeRSConnection { async fn read(&mut self, address: u64, size: usize) -> Result, TockloaderError> { if !self.is_open() { return Err(InternalError::ConnectionNotOpen.into()); @@ -38,8 +38,11 @@ impl IOCommands for ProbeRSConnection { loader.commit(session, options)?; Ok(()) } +} - async fn list_apps( +#[async_trait] +impl IOCommands for ProbeRSConnection { + async fn read_installed_apps( &mut self, settings: &BoardSettings, ) -> Result, TockloaderError> { @@ -60,7 +63,6 @@ impl IOCommands for ProbeRSConnection { let mut core = session.core(self.target_info.core)?; - // TODO(george-cosma): extract these informations without bootloader let system_attributes = SystemAttributes::read_system_attributes_probe(&mut core)?; Ok(system_attributes) } diff --git a/tockloader-lib/src/command_impl/serial/erase_apps.rs b/tockloader-lib/src/command_impl/serial/erase_apps.rs deleted file mode 100644 index 4e4c87a7..00000000 --- a/tockloader-lib/src/command_impl/serial/erase_apps.rs +++ /dev/null @@ -1,25 +0,0 @@ -use async_trait::async_trait; - -use crate::board_settings::BoardSettings; -use crate::bootloader_serial::{ - issue_command, ping_bootloader_and_wait_for_response, Command, Response, -}; -use crate::connection::{Connection, SerialConnection}; -use crate::errors::{InternalError, TockloaderError}; -use crate::CommandEraseApps; - -#[async_trait] -impl CommandEraseApps for SerialConnection { - async fn erase_apps(&mut self, settings: &BoardSettings) -> Result<(), TockloaderError> { - if !self.is_open() { - return Err(InternalError::ConnectionNotOpen.into()); - } - let stream = self.stream.as_mut().expect("Board must be open"); - - ping_bootloader_and_wait_for_response(stream).await?; - - let pkt = (settings.start_address as u32).to_le_bytes().to_vec(); - let (_, _) = issue_command(stream, Command::ErasePage, pkt, true, 0, Response::OK).await?; - Ok(()) - } -} diff --git a/tockloader-lib/src/command_impl/serial/io.rs b/tockloader-lib/src/command_impl/serial/io.rs index 948f8e23..02ae0a07 100644 --- a/tockloader-lib/src/command_impl/serial/io.rs +++ b/tockloader-lib/src/command_impl/serial/io.rs @@ -7,11 +7,11 @@ use crate::{ command_impl::reshuffle_apps::PAGE_SIZE, connection::{Connection, SerialConnection}, errors::{InternalError, TockloaderError}, - IOCommands, + IOCommands, IO, }; #[async_trait] -impl IOCommands for SerialConnection { +impl IO for SerialConnection { async fn read(&mut self, address: u64, size: usize) -> Result, TockloaderError> { let mut pkt = (address as u32).to_le_bytes().to_vec(); let length = (size as u16).to_le_bytes().to_vec(); @@ -30,11 +30,10 @@ impl IOCommands for SerialConnection { ) .await?; - // this might be very specific, but when I was testing, i got 8191 bytes instead of 8192 - // this is because of the consecutive ESCAPE_CHAR rule inside issue_command function - if appdata.len() < size { - panic!("Appdata was not read correctly?? We should not reach here"); + // Sanity check that we wrote everything. This was previously failing + // due to not reading enough when encountering double ESCAPE_CHAR. + panic!("Internal Error: When reading from a Serial connection, we read less bytes than requested despite previous checks."); } Ok(appdata) } @@ -42,22 +41,24 @@ impl IOCommands for SerialConnection { async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError> { let stream = self.stream.as_mut().expect("Board must be open"); let mut binary = pkt.clone(); - while !binary.len().is_multiple_of(PAGE_SIZE as usize) { - binary.push(0u8); + + if !binary.len().is_multiple_of(PAGE_SIZE as usize) { + binary.extend(vec![ + 0u8; + PAGE_SIZE as usize - (binary.len() % PAGE_SIZE as usize) + ]); } - let mut page_number = 0; - while (page_number + 1) * PAGE_SIZE <= binary.len() as u32 { - let mut pkt = (address as u32 + page_number * PAGE_SIZE) + for page_number in 0..(binary.len() / PAGE_SIZE as usize) { + let mut pkt = (address as u32 + page_number as u32 * PAGE_SIZE) .to_le_bytes() .to_vec(); pkt.append( &mut binary - [(page_number * PAGE_SIZE) as usize..((page_number + 1) * PAGE_SIZE) as usize] + [(page_number * PAGE_SIZE as usize)..((page_number + 1) * PAGE_SIZE as usize)] .to_vec(), ); let _ = issue_command(stream, Command::WritePage, pkt, true, 0, Response::OK).await?; - page_number += 1; } let pkt = (address as u32 + binary.len() as u32) @@ -67,8 +68,11 @@ impl IOCommands for SerialConnection { let _ = issue_command(stream, Command::ErasePage, pkt, true, 0, Response::OK).await?; Ok(()) } +} - async fn list_apps( +#[async_trait] +impl IOCommands for SerialConnection { + async fn read_installed_apps( &mut self, settings: &BoardSettings, ) -> Result, TockloaderError> { diff --git a/tockloader-lib/src/lib.rs b/tockloader-lib/src/lib.rs index 2b78148d..70d04170 100644 --- a/tockloader-lib/src/lib.rs +++ b/tockloader-lib/src/lib.rs @@ -67,12 +67,15 @@ pub trait CommandEraseApps { } #[async_trait] -pub trait IOCommands: Send { +pub trait IO: Send { async fn read(&mut self, address: u64, size: usize) -> Result, TockloaderError>; async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError>; +} - async fn list_apps( +#[async_trait] +pub trait IOCommands: Send { + async fn read_installed_apps( &mut self, settings: &BoardSettings, ) -> Result, TockloaderError>; diff --git a/tockloader-lib/src/tabs/tab.rs b/tockloader-lib/src/tabs/tab.rs index 9a5f11a4..fe69d54a 100644 --- a/tockloader-lib/src/tabs/tab.rs +++ b/tockloader-lib/src/tabs/tab.rs @@ -44,16 +44,12 @@ impl Tab { let mut data = Vec::new(); archive_file.read_to_end(&mut data).map_err(TabError::IO)?; - // log::info!("read filename {:?}", file_name); - - // log::info!("data? {:?}", data); tbf_files.push(TbfFile { filename: file_name.to_string(), data, }); } } - // panic!(); match metadata { Some(metadata) => Ok(Tab { @@ -79,17 +75,9 @@ impl Tab { } } - // maybe change the parameter into board settings? pub fn extract_binary(&self, arch: &str) -> Result, TockloaderError> { for file in &self.tbf_files { if file.filename.starts_with(arch) { - // make an inquire that shows only relevant configurations? - // flash >= start_addr - // ram >= ??? - // we need a ram parameter in board_settings - // if file.filename.starts_with("cortex-m4.0x00040000.0x20008000") { // here i set it manually for testing - // TODO(adi): this needs a better implementation for rust apps, the tbf is not selected correctly - // should we select the tab manually? with inquire? return Ok(file.data.clone()); } } From a289e619c5cbdb51fd9557973acfc084bdde281d Mon Sep 17 00:00:00 2001 From: Eva Cosma Date: Mon, 20 Oct 2025 23:22:28 +0300 Subject: [PATCH 3/8] wip: restructure reshuffle_apps Signed-off-by: Eva Cosma --- tbf-parser/src/parse.rs | 7 +- .../src/attributes/app_attributes.rs | 34 +- tockloader-lib/src/command_impl/install.rs | 4 +- .../src/command_impl/reshuffle_apps.rs | 474 +++++++++--------- 4 files changed, 259 insertions(+), 260 deletions(-) diff --git a/tbf-parser/src/parse.rs b/tbf-parser/src/parse.rs index 3baec0ee..47c00429 100644 --- a/tbf-parser/src/parse.rs +++ b/tbf-parser/src/parse.rs @@ -203,9 +203,10 @@ pub fn parse_tbf_header( } types::TbfHeaderTypes::TbfHeaderWriteableFlashRegions => { // Length must be a multiple of the size of a region definition. - if tlv_header.length as usize - % mem::size_of::() - == 0 + if (tlv_header.length as usize) + .is_multiple_of(mem::size_of::< + types::TbfHeaderV2WriteableFlashRegion, + >()) { // Calculate how many writeable flash regions // there are specified in this header. diff --git a/tockloader-lib/src/attributes/app_attributes.rs b/tockloader-lib/src/attributes/app_attributes.rs index 0189edb6..0f9344f0 100644 --- a/tockloader-lib/src/attributes/app_attributes.rs +++ b/tockloader-lib/src/attributes/app_attributes.rs @@ -21,25 +21,8 @@ use crate::IO; #[derive(Debug, Clone)] pub struct AppAttributes { pub address: u64, - pub size: u32, - pub index: u8, pub tbf_header: TbfHeader, pub tbf_footers: Vec, - pub installed: bool, - pub is_padding: bool, -} - -impl std::fmt::Display for AppAttributes { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}. {} - start: {:#x}, size: {}", - self.index, - self.tbf_header.get_package_name().unwrap_or(""), - self.address, - self.size - ) - } } /// This structure represents a footer of a Tock application. Currently, footers @@ -62,20 +45,13 @@ impl TbfFooter { impl AppAttributes { pub(crate) fn new( address: u64, - size: u32, - index: u8, header_data: TbfHeader, footers_data: Vec, - installed: bool, ) -> AppAttributes { AppAttributes { address, - size, - index, tbf_header: header_data, tbf_footers: footers_data, - installed, - is_padding: false, } } @@ -178,10 +154,9 @@ impl AppAttributes { footer_offset += footer_info.1 + 4; } - let details: AppAttributes = - AppAttributes::new(appaddr, total_size, apps_counter, header, footers, true); + let details: AppAttributes = AppAttributes::new(appaddr, header, footers); - apps_details.insert(apps_counter.into(), details); + apps_details.insert(apps_counter, details); apps_counter += 1; appaddr += total_size as u64; } @@ -316,10 +291,9 @@ impl AppAttributes { footer_offset += footer_info.1 + 4; } - let details: AppAttributes = - AppAttributes::new(appaddr, total_size, apps_counter, header, footers, true); + let details: AppAttributes = AppAttributes::new(appaddr, header, footers); - apps_details.insert(apps_counter.into(), details); + apps_details.insert(apps_counter, details); apps_counter += 1; appaddr += total_size as u64; } diff --git a/tockloader-lib/src/command_impl/install.rs b/tockloader-lib/src/command_impl/install.rs index 70997ce8..7fb5754b 100644 --- a/tockloader-lib/src/command_impl/install.rs +++ b/tockloader-lib/src/command_impl/install.rs @@ -2,7 +2,7 @@ use async_trait::async_trait; use crate::attributes::app_attributes::AppAttributes; use crate::board_settings::BoardSettings; -use crate::command_impl::reshuffle_apps::{create_pkt, reconstruct_app, reshuffle_apps}; +use crate::command_impl::reshuffle_apps::{create_pkt, reshuffle_apps}; use crate::connection::TockloaderConnection; use crate::errors::TockloaderError; use crate::tabs::tab::Tab; @@ -15,10 +15,8 @@ impl CommandInstall for TockloaderConnection { settings: &BoardSettings, tab: Tab, ) -> Result<(), TockloaderError> { - // get the already installed apps let mut installed_apps: Vec = self.list(settings).await.unwrap(); - // reconstruct the new app if let Some(mut app) = reconstruct_app(Some(&tab), settings) { app.index = installed_apps.len() as u8; installed_apps.push(app.clone()); diff --git a/tockloader-lib/src/command_impl/reshuffle_apps.rs b/tockloader-lib/src/command_impl/reshuffle_apps.rs index 5ea3eb10..c77543e2 100644 --- a/tockloader-lib/src/command_impl/reshuffle_apps.rs +++ b/tockloader-lib/src/command_impl/reshuffle_apps.rs @@ -1,256 +1,282 @@ use itertools::Itertools; -use tbf_parser::parse::{parse_tbf_footer, parse_tbf_header, parse_tbf_header_lengths}; +use log::warn; -use crate::attributes::app_attributes::{AppAttributes, TbfFooter}; +use crate::attributes::app_attributes::AppAttributes; use crate::board_settings::BoardSettings; -use crate::errors::{InternalError, TockloaderError}; use crate::tabs::tab::Tab; const ALIGNMENT: u64 = 1024; -pub const PAGE_SIZE: u32 = 512; +// TODO(eva-cosma): Move this to `BoardSettings` +pub const PAGE_SIZE: u64 = 512; + +pub enum TockApp { + Flexible(FlexibleApp), + Fixed(FixedApp), +} + +struct FlexibleApp { + idx: Option, + size: u64, +} + +struct FixedApp { + idx: Option, + candidate_addresses: Vec, + size: u64, +} + +impl TockApp { + fn replace_idx(&mut self, new_idx: usize) -> Option { + match self { + TockApp::Flexible(flexible_app) => flexible_app.idx.replace(new_idx), + TockApp::Fixed(fixed_app) => fixed_app.idx.replace(new_idx), + } + } + + fn get_idx(&self) -> Option { + match self { + TockApp::Flexible(flexible_app) => flexible_app.idx, + TockApp::Fixed(fixed_app) => fixed_app.idx, + } + } + + fn get_size(&self) -> u64 { + match self { + TockApp::Flexible(flexible_app) => flexible_app.size, + TockApp::Fixed(fixed_app) => fixed_app.size, + } + } + + fn as_intermediate_index(&self, install_address: u64) -> IntermediateIndex { + IntermediateIndex { + idx: self.get_idx(), + address: install_address, + size: self.get_size(), + } + } + + pub fn from_app_attributes(_app_attributes: &AppAttributes) -> Self { + todo!("Implement TockApp::from_app_attributes") + } + + pub fn from_tab(_tab: &Tab) -> Self { + todo!("Implement TockApp::from_tab") + } +} + +impl FixedApp { + fn as_intermediate_index(&self, install_address: u64) -> IntermediateIndex { + IntermediateIndex { + idx: self.idx, + address: install_address, + size: self.size, + } + } +} + +impl FlexibleApp { + fn as_intermediate_index(&self, install_address: u64) -> IntermediateIndex { + IntermediateIndex { + idx: self.idx, + address: install_address, + size: self.size, + } + } +} + +#[derive(Debug, Clone)] +pub struct Index { + idx: usize, + address: u64, + size: u64, +} + +#[derive(Debug, Clone)] +struct IntermediateIndex { + /// Is none if this is a padding app. + idx: Option, + address: u64, + size: u64, +} + +// c or rust (fixed address?) +// size +// if rust +// flash_address(es) to install or currently installed +// if c +// flash_address currently installed pub fn reshuffle_apps( settings: &BoardSettings, - installed_apps: Vec, -) -> Result, TockloaderError> { - // separate the apps - let mut rust_apps: Vec = Vec::new(); - let mut c_apps: Vec = Vec::new(); - for app in installed_apps.iter() { - match app.tbf_header.get_fixed_address_flash() { - Some(_) => { - rust_apps.push(app.clone()); - } - None => { - c_apps.push(app.clone()); - } + mut installed_apps: Vec, +) -> Option> { + // On the first pass, we must assign every app its original index, so we can + // keep track of it. + for (idx, app) in installed_apps.iter_mut().enumerate() { + if let Some(_) = app.replace_idx(idx) { + warn!("Encountered existing index in TockApp at the start of reorder_apps."); + } + } + + let mut rust_apps: Vec<&mut FixedApp> = Vec::new(); + let mut c_apps: Vec<&mut FlexibleApp> = Vec::new(); + + for app in &mut installed_apps { + match app { + TockApp::Flexible(flexible_app) => c_apps.push(flexible_app), + TockApp::Fixed(fixed_app) => rust_apps.push(fixed_app), + } + } + + for app in &mut rust_apps { + if app.candidate_addresses.len() == 0 { + warn!("Can not reorder apps since fixed application has no candidate addresses!"); + return None; + } + + // TODO(eva-cosma): Remove this requirement + + // For now this algorithm only supports pre-chosen addresses for fixed apps. + // We will keep only the first address around. + if app.candidate_addresses.len() > 1 { + let first = app.candidate_addresses[0]; + app.candidate_addresses.clear(); + app.candidate_addresses.push(first); } } // this is necessary. If a rust app is already installed, for example: at 0x48000 // and we want to install another one at 0x40000, reorder them first - rust_apps.sort_by_key(|app| app.address); + rust_apps.sort_by_key(|app| app.candidate_addresses[0]); // make permutations only for the c apps, as their order can be changed let mut permutations = (0..c_apps.len()).permutations(c_apps.len()); let mut min_padding = usize::MAX; - let mut saved_configuration: Vec = Vec::new(); - - for _ in 0..100_000 { - // use just 100k permutations, or else we'll be here for a while - match permutations.next() { - Some(order) => { - let mut total_padding: usize = 0; - let mut permutation_index: usize = 0; - let mut rust_index: usize = 0; - let mut reordered_apps: Vec = Vec::new(); - loop { - let insert_c: bool; // every iteration will insert an app, or break if there are none left - - // start either where the last app ends, or at start address if there are no apps - let address = match reordered_apps.last() { - Some(app) => app.address + app.size as u64, - None => settings.start_address, - }; - - match order.get(permutation_index) { - Some(_) => { - // we have a C app - match rust_apps.get(rust_index) { - Some(_) => { - // we also have a rust app, insert only if it fits - insert_c = c_apps[order[permutation_index]].size - <= (rust_apps[rust_index].address - address) as u32; - } - None => { - // we have only a C app, insert it accordingly - insert_c = true; - } - } - } - None => { - // we don't have a c app - match rust_apps.get(rust_index) { - Some(_) => { - // we have a rust app, insert it - insert_c = false; - } - None => { - // we don't have any app, break? - break; - } - } - } - } - - let mut start_address: u64; - if reordered_apps.is_empty() { - // is padding needed when starting from settings.start_address? - start_address = settings.start_address; - } else { - // start the padding where last app ends - let last_app = reordered_apps.last().unwrap(); - start_address = last_app.address + last_app.size as u64; - } - let needed_padding: u32 = if insert_c { - if !start_address.is_multiple_of(PAGE_SIZE as u64) { - PAGE_SIZE - start_address as u32 % PAGE_SIZE // c app needs to be inserted at a multiple of page_size - } else { - 0 - } - } else { - if rust_apps[rust_index].address < start_address { - // the program wants to insert a rust app where another rust app already exists - panic!("Can't insert the rust app, space is already occupied by another rust app"); - // we can't change the start address, so panic - } - (rust_apps[rust_index].address - start_address) as u32 - // rust app needs to be inserted at a fixed address, pad until there - }; - if needed_padding > 0 { - // insert a padding - total_padding += needed_padding as usize; - reordered_apps.push(installed_apps[0].clone()); - reordered_apps.last_mut().unwrap().address = start_address; - reordered_apps.last_mut().unwrap().size = needed_padding; - reordered_apps.last_mut().unwrap().is_padding = true; - start_address += needed_padding as u64; - } - if insert_c { - // insert the c app, also change its address - reordered_apps.push(c_apps[order[permutation_index]].clone()); - reordered_apps.last_mut().unwrap().address = start_address; - permutation_index += 1; - } else { - // insert the rust app, don't change its address because it is fixed - reordered_apps.push(rust_apps[rust_index].clone()); - rust_index += 1; - } - } + let mut saved_configuration: Vec = Vec::new(); - // find the configuration that uses the minimum padding - if total_padding < min_padding { - min_padding = total_padding; - saved_configuration = reordered_apps.clone(); - } - } - None => break, - } + if c_apps.len() > 9 && !rust_apps.is_empty() { + warn!( + "Refusing to compute order if more than 9 c-based apps are installed \ + with rust-based apps in the mix! Too computationally heavy!" + ); + return None; } - log::debug!("min padding is {min_padding}"); - // panic!(); - let mut index = 0; - for item in saved_configuration.iter() { - if item.is_padding { - log::debug!( - "-----PADDING APP------, start:{:#x}, size {}, end {:#x}", - item.address, - item.size, - item.address + item.size as u64 - ); - } else { - log::debug!( - "{}. {}, start address {:#x}, size {}, rustapp {}, end address {:#x}", - index, - item.tbf_header.get_package_name().unwrap_or(""), - item.address, - item.size, - match item.tbf_header.get_fixed_address_flash() { - Some(_) => { - true - } - None => { - false - } - }, - item.address + item.size as u64, - ); - index += 1; - } - } - Ok(saved_configuration) -} + while let Some(order) = permutations.next() { + let mut total_padding: usize = 0; + let mut permutation_index: usize = 0; + let mut rust_index: usize = 0; + let mut reordered_apps: Vec = Vec::new(); -// this function takes a tab and turns it into an AppAttributes instance -pub fn reconstruct_app(tab: Option<&Tab>, settings: &BoardSettings) -> Option { - if let Some(tab) = tab { - let arch = settings - .arch - .as_ref() - .ok_or(InternalError::MisconfiguredBoardSettings( - "architechture".to_owned(), - )) - .unwrap(); - - // extract the binary - let binary = tab.extract_binary(arch).expect("invalid arch"); - - // extract relevant data from the header - let (tbf_version, header_len, total_size) = match parse_tbf_header_lengths( - &binary[0..8] - .try_into() - .expect("Buffer length must be at least 8 bytes long."), - ) { - Ok((tbf_version, header_len, total_size)) if header_len != 0 => { - (tbf_version, header_len, total_size) - } - _ => return None, - }; - - // turn the buffer slice into a TbfHeader instance - let header = - parse_tbf_header(&binary[0..header_len as usize], tbf_version).expect("invalid header"); - let binary_end_offset = header.get_binary_end(); - - // obtain the footers - let mut footers: Vec = vec![]; - let mut footer_offset = binary_end_offset; - let mut footer_number = 0; - - while footer_offset < total_size { - let mut appfooter: Vec = binary[footer_offset as usize..].to_vec(); - let byte1 = *appfooter.get(2).unwrap(); - let byte2 = *appfooter.get(3).unwrap(); - let expected_size = u16::from_le_bytes([byte1, byte2]) + 4; - // insert or remove until we have expected size? - // insert - while appfooter.len() < expected_size as usize { - appfooter.push(0x0u8); - } - // delete - while appfooter.len() > expected_size as usize { - appfooter.pop(); + loop { + let insert_c: bool; // every iteration will insert an app, or break if there are none left + + // start either where the last app ends, or at start address if there are no apps + let address = reordered_apps + .last() + .map_or(settings.start_address, |app| app.address + app.size); + + if let Some(_) = order.get(permutation_index) { + // we have a C app + if let Some(_) = rust_apps.get(rust_index) { + // we also have a rust app, insert only if it fits + insert_c = c_apps[order[permutation_index]].size + <= rust_apps[rust_index].candidate_addresses[0] - address; + } else { + // we have only a C app, insert it accordingly + insert_c = true; + } + } else { + // we don't have a c app + if let Some(_) = rust_apps.get(rust_index) { + // we have a rust app, insert it + insert_c = false; + } else { + // we don't have any app, break? + break; + } } - // (these might be useless) - let footer_info = parse_tbf_footer(&appfooter).expect("Invalid footer!"); - footers.insert(footer_number, TbfFooter::new(footer_info.0, footer_info.1)); - footer_number += 1; - footer_offset += footer_info.1 + 4; - } + let mut start_address = reordered_apps + .last() + .map_or(settings.start_address, |app| app.address + app.size); + + let needed_padding = if insert_c { + if !start_address.is_multiple_of(PAGE_SIZE) { + // c app needs to be inserted at a multiple of page_size + PAGE_SIZE - start_address % PAGE_SIZE + } else { + 0 + } + } else { + if rust_apps[rust_index].candidate_addresses[0] < start_address { + // the program wants to insert a rust app where another rust app already exists + warn!( + "Can't insert the rust app, space is already occupied by another rust app" + ); + return None; + } + // rust app needs to be inserted at a fixed address, pad until there + rust_apps[rust_index].candidate_addresses[0] - start_address + }; + + if needed_padding > 0 { + // insert a padding + total_padding += needed_padding as usize; + reordered_apps.push(IntermediateIndex { + idx: None, + address: start_address, + size: needed_padding, + }); + + start_address += needed_padding as u64; + } - // create an AppAttribute using the data we obtained - return Some(AppAttributes::new( - if let Some(addr) = header.get_fixed_address_flash() { - if addr < settings.start_address as u32 { - // this rust app should not be here - panic!("This rust app starts at {addr:#x}, while the board's start_address is {:#x}", settings.start_address) + if insert_c { + // insert the c app, also change its address + let c_app = c_apps[order[permutation_index]].as_intermediate_index(start_address); + if c_app.idx.is_none() { + panic!("C app has no index assigned!"); } - // turns out that fixed address is a loosely-used term, address has to be aligned down to a multiple of 1024 bytes - align_down(addr as u64) + + reordered_apps.push(c_app); + permutation_index += 1; } else { - settings.start_address - }, - total_size, - 0, - header, - footers, - false, - )); + // insert the rust app, don't change its address because it is fixed + let rust_app = rust_apps[rust_index] + .as_intermediate_index(rust_apps[rust_index].candidate_addresses[0]); + if rust_app.idx.is_none() { + panic!("Rust app has no index assigned!"); + } + + reordered_apps.push(rust_app); + rust_index += 1; + } + } + + // find the configuration that uses the minimum padding + if total_padding < min_padding { + min_padding = total_padding; + saved_configuration = reordered_apps.clone(); + if min_padding == 0 { + break; + } + } } - None + + let result = saved_configuration + .iter() + .filter_map(|iindex| { + iindex.idx.map(|idx| Index { + idx, + address: iindex.address, + size: iindex.size, + }) + }) + .collect::>(); + + Some(result) } // this return only the binary for a padding From 66965d1916dea82afef462a5453556aa4e63cee8 Mon Sep 17 00:00:00 2001 From: addrian-77 Date: Thu, 30 Oct 2025 15:37:53 +0200 Subject: [PATCH 4/8] wip: refactored reshuffle Signed-off-by: addrian-77 --- .../src/attributes/app_attributes.rs | 6 - tockloader-lib/src/board_settings.rs | 2 + tockloader-lib/src/command_impl/erase_apps.rs | 3 +- .../src/command_impl/generalized.rs | 11 +- tockloader-lib/src/command_impl/install.rs | 43 ++-- tockloader-lib/src/command_impl/probers/io.rs | 7 +- .../src/command_impl/reshuffle_apps.rs | 204 +++++++++++++----- tockloader-lib/src/command_impl/serial/io.rs | 21 +- tockloader-lib/src/known_boards.rs | 2 + tockloader-lib/src/lib.rs | 7 +- 10 files changed, 215 insertions(+), 91 deletions(-) diff --git a/tockloader-lib/src/attributes/app_attributes.rs b/tockloader-lib/src/attributes/app_attributes.rs index 0f9344f0..29aa3bdc 100644 --- a/tockloader-lib/src/attributes/app_attributes.rs +++ b/tockloader-lib/src/attributes/app_attributes.rs @@ -11,7 +11,6 @@ use tokio_serial::SerialStream; use crate::bootloader_serial::{issue_command, Command, Response}; use crate::errors::{TockError, TockloaderError}; -use crate::IO; /// This structure contains all relevant information about a tock application. /// @@ -299,9 +298,4 @@ impl AppAttributes { } Ok(apps_details) } - - /// This function reads the full binary of a given app - pub async fn read_binary(&mut self, conn: &mut dyn IO) -> Result, TockloaderError> { - conn.read(self.address, self.size as usize).await - } } diff --git a/tockloader-lib/src/board_settings.rs b/tockloader-lib/src/board_settings.rs index d7f67ab8..f9bfec5b 100644 --- a/tockloader-lib/src/board_settings.rs +++ b/tockloader-lib/src/board_settings.rs @@ -1,6 +1,7 @@ pub struct BoardSettings { pub arch: Option, pub start_address: u64, + pub page_size: u64, } // TODO(george-cosma): Does a default implementation make sense for this? Is a @@ -10,6 +11,7 @@ impl Default for BoardSettings { Self { arch: None, start_address: 0x30000, + page_size: 512, } } } diff --git a/tockloader-lib/src/command_impl/erase_apps.rs b/tockloader-lib/src/command_impl/erase_apps.rs index 100926c5..fe622fdd 100644 --- a/tockloader-lib/src/command_impl/erase_apps.rs +++ b/tockloader-lib/src/command_impl/erase_apps.rs @@ -8,6 +8,7 @@ use crate::{CommandEraseApps, IO}; #[async_trait] impl CommandEraseApps for TockloaderConnection { async fn erase_apps(&mut self, settings: &BoardSettings) -> Result<(), TockloaderError> { - self.write(settings.start_address, vec![0u8]).await + self.write(settings.start_address, vec![0u8], settings) + .await } } diff --git a/tockloader-lib/src/command_impl/generalized.rs b/tockloader-lib/src/command_impl/generalized.rs index 574cae80..1f2fa901 100644 --- a/tockloader-lib/src/command_impl/generalized.rs +++ b/tockloader-lib/src/command_impl/generalized.rs @@ -16,10 +16,15 @@ impl IO for TockloaderConnection { } } - async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError> { + async fn write( + &mut self, + address: u64, + pkt: Vec, + settings: &BoardSettings, + ) -> Result<(), TockloaderError> { match self { - TockloaderConnection::ProbeRS(conn) => conn.write(address, pkt).await, - TockloaderConnection::Serial(conn) => conn.write(address, pkt).await, + TockloaderConnection::ProbeRS(conn) => conn.write(address, pkt, settings).await, + TockloaderConnection::Serial(conn) => conn.write(address, pkt, settings).await, } } } diff --git a/tockloader-lib/src/command_impl/install.rs b/tockloader-lib/src/command_impl/install.rs index 7fb5754b..af0c5f58 100644 --- a/tockloader-lib/src/command_impl/install.rs +++ b/tockloader-lib/src/command_impl/install.rs @@ -2,7 +2,7 @@ use async_trait::async_trait; use crate::attributes::app_attributes::AppAttributes; use crate::board_settings::BoardSettings; -use crate::command_impl::reshuffle_apps::{create_pkt, reshuffle_apps}; +use crate::command_impl::reshuffle_apps::{create_pkt, reshuffle_apps, TockApp}; use crate::connection::TockloaderConnection; use crate::errors::TockloaderError; use crate::tabs::tab::Tab; @@ -15,37 +15,36 @@ impl CommandInstall for TockloaderConnection { settings: &BoardSettings, tab: Tab, ) -> Result<(), TockloaderError> { - let mut installed_apps: Vec = self.list(settings).await.unwrap(); - - if let Some(mut app) = reconstruct_app(Some(&tab), settings) { - app.index = installed_apps.len() as u8; - installed_apps.push(app.clone()); - } + let app_attributes_list: Vec = self.list(settings).await.unwrap(); + let mut tock_app_list = app_attributes_list + .iter() + .map(|app| TockApp::from_app_attributes(app, settings)) + .collect::>(); // obtain the binaries in a vector let mut app_binaries: Vec> = Vec::new(); - for app in installed_apps.iter() { - match app.installed { - true => { - app_binaries.push(app.clone().read_binary(self).await.unwrap()); - } - false => { - // TODO(adi): change this when TBF Filtering will get merged - app_binaries.push( - tab.extract_binary(settings.arch.as_ref().unwrap().as_str()) - .unwrap(), - ); - } - } + for app in tock_app_list.iter() { + app_binaries.push(app.clone().read_binary(self).await.unwrap()) } - let configuration = reshuffle_apps(settings, installed_apps).unwrap(); + + let mut app = TockApp::from_tab(&tab, settings).unwrap(); + + app.replace_idx(tock_app_list.len()); + tock_app_list.push(app.clone()); + + app_binaries.push( + tab.extract_binary(settings.arch.as_ref().unwrap().as_str()) + .unwrap(), + ); + + let configuration = reshuffle_apps(settings, tock_app_list).unwrap(); // create the pkt, this contains all the binaries in a vec let pkt = create_pkt(configuration, app_binaries); // write the pkt - let _ = self.write(settings.start_address, pkt).await; + let _ = self.write(settings.start_address, pkt, settings).await; Ok(()) } } diff --git a/tockloader-lib/src/command_impl/probers/io.rs b/tockloader-lib/src/command_impl/probers/io.rs index 0f99df28..09105773 100644 --- a/tockloader-lib/src/command_impl/probers/io.rs +++ b/tockloader-lib/src/command_impl/probers/io.rs @@ -23,7 +23,12 @@ impl IO for ProbeRSConnection { Ok(appdata) } - async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError> { + async fn write( + &mut self, + address: u64, + pkt: Vec, + _settings: &BoardSettings, + ) -> Result<(), TockloaderError> { if !self.is_open() { return Err(InternalError::ConnectionNotOpen.into()); } diff --git a/tockloader-lib/src/command_impl/reshuffle_apps.rs b/tockloader-lib/src/command_impl/reshuffle_apps.rs index c77543e2..7da2c53e 100644 --- a/tockloader-lib/src/command_impl/reshuffle_apps.rs +++ b/tockloader-lib/src/command_impl/reshuffle_apps.rs @@ -1,38 +1,45 @@ use itertools::Itertools; use log::warn; -use crate::attributes::app_attributes::AppAttributes; +use crate::attributes::app_attributes::{AppAttributes, TbfFooter}; use crate::board_settings::BoardSettings; +use crate::errors::{InternalError, TockloaderError}; use crate::tabs::tab::Tab; +use crate::IO; +use tbf_parser::parse::{parse_tbf_footer, parse_tbf_header, parse_tbf_header_lengths}; const ALIGNMENT: u64 = 1024; -// TODO(eva-cosma): Move this to `BoardSettings` -pub const PAGE_SIZE: u64 = 512; +#[derive(Clone)] pub enum TockApp { Flexible(FlexibleApp), Fixed(FixedApp), } -struct FlexibleApp { +#[derive(Clone)] +pub struct FlexibleApp { idx: Option, + board_address: Option, // None if not installed size: u64, } -struct FixedApp { +#[derive(Clone)] +pub struct FixedApp { idx: Option, + board_address: Option, // None if not installed candidate_addresses: Vec, size: u64, } impl TockApp { - fn replace_idx(&mut self, new_idx: usize) -> Option { + pub fn replace_idx(&mut self, new_idx: usize) -> Option { match self { TockApp::Flexible(flexible_app) => flexible_app.idx.replace(new_idx), TockApp::Fixed(fixed_app) => fixed_app.idx.replace(new_idx), } } + // useless?? fn get_idx(&self) -> Option { match self { TockApp::Flexible(flexible_app) => flexible_app.idx, @@ -40,6 +47,7 @@ impl TockApp { } } + // useless? fn get_size(&self) -> u64 { match self { TockApp::Flexible(flexible_app) => flexible_app.size, @@ -47,26 +55,142 @@ impl TockApp { } } - fn as_intermediate_index(&self, install_address: u64) -> IntermediateIndex { - IntermediateIndex { + // useless? + fn as_index(&self, install_address: u64) -> Index { + Index { idx: self.get_idx(), address: install_address, size: self.get_size(), } } - pub fn from_app_attributes(_app_attributes: &AppAttributes) -> Self { - todo!("Implement TockApp::from_app_attributes") + pub fn from_app_attributes( + app_attributes: &AppAttributes, + settings: &BoardSettings, + ) -> TockApp { + if let Some(address) = app_attributes.tbf_header.get_fixed_address_flash() { + return TockApp::Fixed(FixedApp { + idx: None, + board_address: None, + candidate_addresses: vec![address as u64], // (adi): change this when tbf selector gets merged + size: app_attributes.tbf_header.total_size() as u64, + }); + } else { + let address = settings.start_address; + return TockApp::Flexible(FlexibleApp { + idx: None, + board_address: Some(address), + size: app_attributes.tbf_header.total_size() as u64, + }); + } + } + + pub fn from_tab(tab: &Tab, settings: &BoardSettings) -> Option { + // tab: Option<&Tab>, settings: &BoardSettings) -> Option + let arch = settings + .arch + .as_ref() + .ok_or(InternalError::MisconfiguredBoardSettings( + "architechture".to_owned(), + )) + .unwrap(); + + // extract the binary + // this should be changed to accomodate candidate_addresses + let binary = tab.extract_binary(arch).expect("invalid arch"); + + // extract relevant data from the header + let (tbf_version, header_len, total_size) = match parse_tbf_header_lengths( + &binary[0..8] + .try_into() + .expect("Buffer length must be at least 8 bytes long."), + ) { + Ok((tbf_version, header_len, total_size)) if header_len != 0 => { + (tbf_version, header_len, total_size) + } + _ => return None, + }; + + // turn the buffer slice into a TbfHeader instance + let header = + parse_tbf_header(&binary[0..header_len as usize], tbf_version).expect("invalid header"); + let binary_end_offset = header.get_binary_end(); + + // obtain the footers + let mut footers: Vec = vec![]; + let mut footer_offset = binary_end_offset; + let mut footer_number = 0; + + while footer_offset < total_size { + let mut appfooter: Vec = binary[footer_offset as usize..].to_vec(); + let byte1 = *appfooter.get(2).unwrap(); + let byte2 = *appfooter.get(3).unwrap(); + let expected_size = u16::from_le_bytes([byte1, byte2]) + 4; + // insert or remove until we have expected size? + // insert + while appfooter.len() < expected_size as usize { + appfooter.push(0x0u8); + } + // delete + while appfooter.len() > expected_size as usize { + appfooter.pop(); + } + // (these might be useless) + + let footer_info = parse_tbf_footer(&appfooter).expect("Invalid footer!"); + footers.insert(footer_number, TbfFooter::new(footer_info.0, footer_info.1)); + footer_number += 1; + footer_offset += footer_info.1 + 4; + } + + if let Some(addr) = header.get_fixed_address_flash() { + if addr < settings.start_address as u32 { + // this rust app should not be here + panic!( + "This rust app starts at {addr:#x}, while the board's start_address is {:#x}", + settings.start_address + ) + } + // turns out that fixed address is a loosely-used term, address has to be aligned down to a multiple of 1024 bytes + let address = align_down(addr as u64); + + return Some(TockApp::Fixed(FixedApp { + idx: None, + board_address: None, + candidate_addresses: vec![address], // (adi): change this when tbf selector gets merged + size: total_size as u64, + })); + } else { + let address = settings.start_address; + return Some(TockApp::Flexible(FlexibleApp { + idx: None, + board_address: Some(address), + size: total_size as u64, + })); + } } - pub fn from_tab(_tab: &Tab) -> Self { - todo!("Implement TockApp::from_tab") + /// This function reads the full binary of a given app + pub async fn read_binary(&mut self, conn: &mut dyn IO) -> Result, TockloaderError> { + match self { + TockApp::Flexible(flexible_app) => { + conn.read( + flexible_app.board_address.unwrap(), + flexible_app.size as usize, + ) + .await + } + TockApp::Fixed(fixed_app) => { + conn.read(fixed_app.board_address.unwrap(), fixed_app.size as usize) + .await + } + } } } impl FixedApp { - fn as_intermediate_index(&self, install_address: u64) -> IntermediateIndex { - IntermediateIndex { + fn as_index(&self, install_address: u64) -> Index { + Index { idx: self.idx, address: install_address, size: self.size, @@ -75,8 +199,8 @@ impl FixedApp { } impl FlexibleApp { - fn as_intermediate_index(&self, install_address: u64) -> IntermediateIndex { - IntermediateIndex { + fn as_index(&self, install_address: u64) -> Index { + Index { idx: self.idx, address: install_address, size: self.size, @@ -84,16 +208,9 @@ impl FlexibleApp { } } +// a vec of these is returned by reshuffleapps #[derive(Debug, Clone)] pub struct Index { - idx: usize, - address: u64, - size: u64, -} - -#[derive(Debug, Clone)] -struct IntermediateIndex { - /// Is none if this is a padding app. idx: Option, address: u64, size: u64, @@ -153,7 +270,7 @@ pub fn reshuffle_apps( let mut permutations = (0..c_apps.len()).permutations(c_apps.len()); let mut min_padding = usize::MAX; - let mut saved_configuration: Vec = Vec::new(); + let mut saved_configuration: Vec = Vec::new(); if c_apps.len() > 9 && !rust_apps.is_empty() { warn!( @@ -167,7 +284,7 @@ pub fn reshuffle_apps( let mut total_padding: usize = 0; let mut permutation_index: usize = 0; let mut rust_index: usize = 0; - let mut reordered_apps: Vec = Vec::new(); + let mut reordered_apps: Vec = Vec::new(); loop { let insert_c: bool; // every iteration will insert an app, or break if there are none left @@ -180,7 +297,7 @@ pub fn reshuffle_apps( if let Some(_) = order.get(permutation_index) { // we have a C app if let Some(_) = rust_apps.get(rust_index) { - // we also have a rust app, insert only if it fits + // we also have a rust app, insert C app only if it fits insert_c = c_apps[order[permutation_index]].size <= rust_apps[rust_index].candidate_addresses[0] - address; } else { @@ -203,9 +320,9 @@ pub fn reshuffle_apps( .map_or(settings.start_address, |app| app.address + app.size); let needed_padding = if insert_c { - if !start_address.is_multiple_of(PAGE_SIZE) { + if !start_address.is_multiple_of(settings.page_size) { // c app needs to be inserted at a multiple of page_size - PAGE_SIZE - start_address % PAGE_SIZE + settings.page_size - start_address % settings.page_size } else { 0 } @@ -224,7 +341,7 @@ pub fn reshuffle_apps( if needed_padding > 0 { // insert a padding total_padding += needed_padding as usize; - reordered_apps.push(IntermediateIndex { + reordered_apps.push(Index { idx: None, address: start_address, size: needed_padding, @@ -235,7 +352,7 @@ pub fn reshuffle_apps( if insert_c { // insert the c app, also change its address - let c_app = c_apps[order[permutation_index]].as_intermediate_index(start_address); + let c_app = c_apps[order[permutation_index]].as_index(start_address); if c_app.idx.is_none() { panic!("C app has no index assigned!"); } @@ -244,8 +361,8 @@ pub fn reshuffle_apps( permutation_index += 1; } else { // insert the rust app, don't change its address because it is fixed - let rust_app = rust_apps[rust_index] - .as_intermediate_index(rust_apps[rust_index].candidate_addresses[0]); + let rust_app = + rust_apps[rust_index].as_index(rust_apps[rust_index].candidate_addresses[0]); if rust_app.idx.is_none() { panic!("Rust app has no index assigned!"); } @@ -265,18 +382,7 @@ pub fn reshuffle_apps( } } - let result = saved_configuration - .iter() - .filter_map(|iindex| { - iindex.idx.map(|idx| Index { - idx, - address: iindex.address, - size: iindex.size, - }) - }) - .collect::>(); - - Some(result) + Some(saved_configuration) } // this return only the binary for a padding @@ -302,15 +408,15 @@ fn align_down(address: u64) -> u64 { address - address % ALIGNMENT } -pub fn create_pkt(configuration: Vec, mut app_binaries: Vec>) -> Vec { +pub fn create_pkt(configuration: Vec, mut app_binaries: Vec>) -> Vec { let mut pkt: Vec = Vec::new(); for item in configuration.iter() { - if item.is_padding { + if item.idx.is_none() { // write padding binary - let mut buf = create_padding(item.size); + let mut buf = create_padding(item.size as u32); pkt.append(&mut buf); } else { - pkt.append(&mut app_binaries[item.index as usize]); + pkt.append(&mut app_binaries[item.idx.unwrap()]); } } pkt diff --git a/tockloader-lib/src/command_impl/serial/io.rs b/tockloader-lib/src/command_impl/serial/io.rs index 02ae0a07..0efbee7a 100644 --- a/tockloader-lib/src/command_impl/serial/io.rs +++ b/tockloader-lib/src/command_impl/serial/io.rs @@ -4,7 +4,6 @@ use crate::{ attributes::{app_attributes::AppAttributes, system_attributes::SystemAttributes}, board_settings::BoardSettings, bootloader_serial::{issue_command, ping_bootloader_and_wait_for_response, Command, Response}, - command_impl::reshuffle_apps::PAGE_SIZE, connection::{Connection, SerialConnection}, errors::{InternalError, TockloaderError}, IOCommands, IO, @@ -38,24 +37,30 @@ impl IO for SerialConnection { Ok(appdata) } - async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError> { + async fn write( + &mut self, + address: u64, + pkt: Vec, + settings: &BoardSettings, + ) -> Result<(), TockloaderError> { let stream = self.stream.as_mut().expect("Board must be open"); let mut binary = pkt.clone(); - if !binary.len().is_multiple_of(PAGE_SIZE as usize) { + if !binary.len().is_multiple_of(settings.page_size as usize) { binary.extend(vec![ 0u8; - PAGE_SIZE as usize - (binary.len() % PAGE_SIZE as usize) + settings.page_size as usize + - (binary.len() % settings.page_size as usize) ]); } - for page_number in 0..(binary.len() / PAGE_SIZE as usize) { - let mut pkt = (address as u32 + page_number as u32 * PAGE_SIZE) + for page_number in 0..(binary.len() / settings.page_size as usize) { + let mut pkt = (address as u32 + page_number as u32 * settings.page_size as u32) .to_le_bytes() .to_vec(); pkt.append( - &mut binary - [(page_number * PAGE_SIZE as usize)..((page_number + 1) * PAGE_SIZE as usize)] + &mut binary[(page_number * settings.page_size as usize) + ..((page_number + 1) * settings.page_size as usize)] .to_vec(), ); let _ = issue_command(stream, Command::WritePage, pkt, true, 0, Response::OK).await?; diff --git a/tockloader-lib/src/known_boards.rs b/tockloader-lib/src/known_boards.rs index 52dc0bac..6a986273 100644 --- a/tockloader-lib/src/known_boards.rs +++ b/tockloader-lib/src/known_boards.rs @@ -25,6 +25,7 @@ impl KnownBoard for NucleoF4 { BoardSettings { arch: Some("cortex-m4".to_string()), start_address: 0x08040000, + page_size: 512, } } } @@ -47,6 +48,7 @@ impl KnownBoard for MicrobitV2 { BoardSettings { arch: Some("cortex-m4".to_string()), start_address: 0x00040000, + page_size: 512, } } } diff --git a/tockloader-lib/src/lib.rs b/tockloader-lib/src/lib.rs index 70d04170..22167472 100644 --- a/tockloader-lib/src/lib.rs +++ b/tockloader-lib/src/lib.rs @@ -70,7 +70,12 @@ pub trait CommandEraseApps { pub trait IO: Send { async fn read(&mut self, address: u64, size: usize) -> Result, TockloaderError>; - async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError>; + async fn write( + &mut self, + address: u64, + pkt: Vec, + settings: &BoardSettings, + ) -> Result<(), TockloaderError>; } #[async_trait] From 285bd6130b4e90d1e1828da5337027833b7e094a Mon Sep 17 00:00:00 2001 From: addrian-77 Date: Thu, 30 Oct 2025 23:25:22 +0200 Subject: [PATCH 5/8] refactor: remove read_binary Signed-off-by: addrian-77 --- .../src/attributes/app_attributes.rs | 2 +- tockloader-lib/src/command_impl/install.rs | 17 +- .../src/command_impl/reshuffle_apps.rs | 323 ++++++------------ tockloader-lib/src/tabs/tab.rs | 4 +- 4 files changed, 125 insertions(+), 221 deletions(-) diff --git a/tockloader-lib/src/attributes/app_attributes.rs b/tockloader-lib/src/attributes/app_attributes.rs index 29aa3bdc..654795ab 100644 --- a/tockloader-lib/src/attributes/app_attributes.rs +++ b/tockloader-lib/src/attributes/app_attributes.rs @@ -17,7 +17,7 @@ use crate::errors::{TockError, TockloaderError}; /// All data is stored either within [TbfHeader]s, or [TbfFooter]s. /// /// See also -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct AppAttributes { pub address: u64, pub tbf_header: TbfHeader, diff --git a/tockloader-lib/src/command_impl/install.rs b/tockloader-lib/src/command_impl/install.rs index af0c5f58..2f95c4f4 100644 --- a/tockloader-lib/src/command_impl/install.rs +++ b/tockloader-lib/src/command_impl/install.rs @@ -18,14 +18,20 @@ impl CommandInstall for TockloaderConnection { let app_attributes_list: Vec = self.list(settings).await.unwrap(); let mut tock_app_list = app_attributes_list .iter() - .map(|app| TockApp::from_app_attributes(app, settings)) + .map(TockApp::from_app_attributes) .collect::>(); // obtain the binaries in a vector let mut app_binaries: Vec> = Vec::new(); - for app in tock_app_list.iter() { - app_binaries.push(app.clone().read_binary(self).await.unwrap()) + let mut address = settings.start_address; + for app in app_attributes_list.iter() { + app_binaries.push( + self.read(address, app.tbf_header.total_size() as usize) + .await + .unwrap(), + ); + address += app.tbf_header.total_size() as u64; } let mut app = TockApp::from_tab(&tab, settings).unwrap(); @@ -33,10 +39,7 @@ impl CommandInstall for TockloaderConnection { app.replace_idx(tock_app_list.len()); tock_app_list.push(app.clone()); - app_binaries.push( - tab.extract_binary(settings.arch.as_ref().unwrap().as_str()) - .unwrap(), - ); + app_binaries.push(tab.extract_binary(settings.arch.clone().unwrap()).unwrap()); let configuration = reshuffle_apps(settings, tock_app_list).unwrap(); diff --git a/tockloader-lib/src/command_impl/reshuffle_apps.rs b/tockloader-lib/src/command_impl/reshuffle_apps.rs index 7da2c53e..2d3dfe67 100644 --- a/tockloader-lib/src/command_impl/reshuffle_apps.rs +++ b/tockloader-lib/src/command_impl/reshuffle_apps.rs @@ -1,12 +1,10 @@ use itertools::Itertools; use log::warn; -use crate::attributes::app_attributes::{AppAttributes, TbfFooter}; +use crate::attributes::app_attributes::AppAttributes; use crate::board_settings::BoardSettings; -use crate::errors::{InternalError, TockloaderError}; use crate::tabs::tab::Tab; -use crate::IO; -use tbf_parser::parse::{parse_tbf_footer, parse_tbf_header, parse_tbf_header_lengths}; +use tbf_parser::parse::{parse_tbf_header, parse_tbf_header_lengths}; const ALIGNMENT: u64 = 1024; @@ -19,14 +17,12 @@ pub enum TockApp { #[derive(Clone)] pub struct FlexibleApp { idx: Option, - board_address: Option, // None if not installed size: u64, } #[derive(Clone)] pub struct FixedApp { idx: Option, - board_address: Option, // None if not installed candidate_addresses: Vec, size: u64, } @@ -39,65 +35,28 @@ impl TockApp { } } - // useless?? - fn get_idx(&self) -> Option { - match self { - TockApp::Flexible(flexible_app) => flexible_app.idx, - TockApp::Fixed(fixed_app) => fixed_app.idx, - } - } - - // useless? - fn get_size(&self) -> u64 { - match self { - TockApp::Flexible(flexible_app) => flexible_app.size, - TockApp::Fixed(fixed_app) => fixed_app.size, - } - } - - // useless? - fn as_index(&self, install_address: u64) -> Index { - Index { - idx: self.get_idx(), - address: install_address, - size: self.get_size(), - } - } - - pub fn from_app_attributes( - app_attributes: &AppAttributes, - settings: &BoardSettings, - ) -> TockApp { + pub fn from_app_attributes(app_attributes: &AppAttributes) -> TockApp { if let Some(address) = app_attributes.tbf_header.get_fixed_address_flash() { - return TockApp::Fixed(FixedApp { + TockApp::Fixed(FixedApp { idx: None, - board_address: None, candidate_addresses: vec![address as u64], // (adi): change this when tbf selector gets merged size: app_attributes.tbf_header.total_size() as u64, - }); + }) } else { - let address = settings.start_address; - return TockApp::Flexible(FlexibleApp { + TockApp::Flexible(FlexibleApp { idx: None, - board_address: Some(address), size: app_attributes.tbf_header.total_size() as u64, - }); + }) } } + /// This function returns an instance of TockApp pub fn from_tab(tab: &Tab, settings: &BoardSettings) -> Option { - // tab: Option<&Tab>, settings: &BoardSettings) -> Option - let arch = settings - .arch - .as_ref() - .ok_or(InternalError::MisconfiguredBoardSettings( - "architechture".to_owned(), - )) - .unwrap(); - // extract the binary // this should be changed to accomodate candidate_addresses - let binary = tab.extract_binary(arch).expect("invalid arch"); + let binary = tab + .extract_binary(settings.arch.clone().unwrap()) + .expect("invalid arch"); // extract relevant data from the header let (tbf_version, header_len, total_size) = match parse_tbf_header_lengths( @@ -114,34 +73,6 @@ impl TockApp { // turn the buffer slice into a TbfHeader instance let header = parse_tbf_header(&binary[0..header_len as usize], tbf_version).expect("invalid header"); - let binary_end_offset = header.get_binary_end(); - - // obtain the footers - let mut footers: Vec = vec![]; - let mut footer_offset = binary_end_offset; - let mut footer_number = 0; - - while footer_offset < total_size { - let mut appfooter: Vec = binary[footer_offset as usize..].to_vec(); - let byte1 = *appfooter.get(2).unwrap(); - let byte2 = *appfooter.get(3).unwrap(); - let expected_size = u16::from_le_bytes([byte1, byte2]) + 4; - // insert or remove until we have expected size? - // insert - while appfooter.len() < expected_size as usize { - appfooter.push(0x0u8); - } - // delete - while appfooter.len() > expected_size as usize { - appfooter.pop(); - } - // (these might be useless) - - let footer_info = parse_tbf_footer(&appfooter).expect("Invalid footer!"); - footers.insert(footer_number, TbfFooter::new(footer_info.0, footer_info.1)); - footer_number += 1; - footer_offset += footer_info.1 + 4; - } if let Some(addr) = header.get_fixed_address_flash() { if addr < settings.start_address as u32 { @@ -154,36 +85,16 @@ impl TockApp { // turns out that fixed address is a loosely-used term, address has to be aligned down to a multiple of 1024 bytes let address = align_down(addr as u64); - return Some(TockApp::Fixed(FixedApp { + Some(TockApp::Fixed(FixedApp { idx: None, - board_address: None, candidate_addresses: vec![address], // (adi): change this when tbf selector gets merged size: total_size as u64, - })); + })) } else { - let address = settings.start_address; - return Some(TockApp::Flexible(FlexibleApp { + Some(TockApp::Flexible(FlexibleApp { idx: None, - board_address: Some(address), size: total_size as u64, - })); - } - } - - /// This function reads the full binary of a given app - pub async fn read_binary(&mut self, conn: &mut dyn IO) -> Result, TockloaderError> { - match self { - TockApp::Flexible(flexible_app) => { - conn.read( - flexible_app.board_address.unwrap(), - flexible_app.size as usize, - ) - .await - } - TockApp::Fixed(fixed_app) => { - conn.read(fixed_app.board_address.unwrap(), fixed_app.size as usize) - .await - } + })) } } } @@ -208,7 +119,6 @@ impl FlexibleApp { } } -// a vec of these is returned by reshuffleapps #[derive(Debug, Clone)] pub struct Index { idx: Option, @@ -216,13 +126,6 @@ pub struct Index { size: u64, } -// c or rust (fixed address?) -// size -// if rust -// flash_address(es) to install or currently installed -// if c -// flash_address currently installed - pub fn reshuffle_apps( settings: &BoardSettings, mut installed_apps: Vec, @@ -230,7 +133,7 @@ pub fn reshuffle_apps( // On the first pass, we must assign every app its original index, so we can // keep track of it. for (idx, app) in installed_apps.iter_mut().enumerate() { - if let Some(_) = app.replace_idx(idx) { + if app.replace_idx(idx).is_some() { warn!("Encountered existing index in TockApp at the start of reorder_apps."); } } @@ -246,7 +149,7 @@ pub fn reshuffle_apps( } for app in &mut rust_apps { - if app.candidate_addresses.len() == 0 { + if app.candidate_addresses.is_empty() { warn!("Can not reorder apps since fixed application has no candidate addresses!"); return None; } @@ -272,120 +175,117 @@ pub fn reshuffle_apps( let mut min_padding = usize::MAX; let mut saved_configuration: Vec = Vec::new(); - if c_apps.len() > 9 && !rust_apps.is_empty() { - warn!( - "Refusing to compute order if more than 9 c-based apps are installed \ - with rust-based apps in the mix! Too computationally heavy!" - ); - return None; - } - - while let Some(order) = permutations.next() { - let mut total_padding: usize = 0; - let mut permutation_index: usize = 0; - let mut rust_index: usize = 0; - let mut reordered_apps: Vec = Vec::new(); - - loop { - let insert_c: bool; // every iteration will insert an app, or break if there are none left - - // start either where the last app ends, or at start address if there are no apps - let address = reordered_apps - .last() - .map_or(settings.start_address, |app| app.address + app.size); - - if let Some(_) = order.get(permutation_index) { - // we have a C app - if let Some(_) = rust_apps.get(rust_index) { - // we also have a rust app, insert C app only if it fits - insert_c = c_apps[order[permutation_index]].size - <= rust_apps[rust_index].candidate_addresses[0] - address; + for _ in 0..100_000 { + // use just 100k permutations, or else we'll be here for a while + if let Some(order) = permutations.next() { + let mut total_padding: usize = 0; + let mut permutation_index: usize = 0; + let mut rust_index: usize = 0; + let mut reordered_apps: Vec = Vec::new(); + + loop { + let insert_c: bool; // every iteration will insert an app, or break if there are none left + + // start either where the last app ends, or at start address if there are no apps + let address = reordered_apps + .last() + .map_or(settings.start_address, |app| app.address + app.size); + + if order.get(permutation_index).is_some() { + // we have a C app + if rust_apps.get(rust_index).is_some() { + // we also have a rust app, insert C app only if it fits + insert_c = c_apps[order[permutation_index]].size + <= rust_apps[rust_index].candidate_addresses[0] - address; + } else { + // we have only a C app, insert it accordingly + insert_c = true; + } } else { - // we have only a C app, insert it accordingly - insert_c = true; + // we don't have a c app + if rust_apps.get(rust_index).is_some() { + // we have a rust app, insert it + insert_c = false; + } else { + // we don't have any app, break? + break; + } } - } else { - // we don't have a c app - if let Some(_) = rust_apps.get(rust_index) { - // we have a rust app, insert it - insert_c = false; + + let mut start_address = reordered_apps + .last() + .map_or(settings.start_address, |app| app.address + app.size); + + let needed_padding = if insert_c { + if !start_address.is_multiple_of(settings.page_size) { + // c app needs to be inserted at a multiple of page_size + settings.page_size - start_address % settings.page_size + } else { + 0 + } } else { - // we don't have any app, break? - break; + if rust_apps[rust_index].candidate_addresses[0] < start_address { + // the program wants to insert a rust app where another rust app already exists + warn!( + "Can't insert the rust app, space is already occupied by another rust app" + ); + return None; + } + // rust app needs to be inserted at a fixed address, pad until there + rust_apps[rust_index].candidate_addresses[0] - start_address + }; + + if needed_padding > 0 { + // insert a padding + total_padding += needed_padding as usize; + reordered_apps.push(Index { + idx: None, + address: start_address, + size: needed_padding, + }); + + start_address += needed_padding as u64; } - } - let mut start_address = reordered_apps - .last() - .map_or(settings.start_address, |app| app.address + app.size); + if insert_c { + // insert the c app, also change its address + let c_app = c_apps[order[permutation_index]].as_index(start_address); + if c_app.idx.is_none() { + panic!("C app has no index assigned!"); + } - let needed_padding = if insert_c { - if !start_address.is_multiple_of(settings.page_size) { - // c app needs to be inserted at a multiple of page_size - settings.page_size - start_address % settings.page_size + reordered_apps.push(c_app); + permutation_index += 1; } else { - 0 - } - } else { - if rust_apps[rust_index].candidate_addresses[0] < start_address { - // the program wants to insert a rust app where another rust app already exists - warn!( - "Can't insert the rust app, space is already occupied by another rust app" - ); - return None; + // insert the rust app, don't change its address because it is fixed + let rust_app = rust_apps[rust_index] + .as_index(rust_apps[rust_index].candidate_addresses[0]); + if rust_app.idx.is_none() { + panic!("Rust app has no index assigned!"); + } + + reordered_apps.push(rust_app); + rust_index += 1; } - // rust app needs to be inserted at a fixed address, pad until there - rust_apps[rust_index].candidate_addresses[0] - start_address - }; - - if needed_padding > 0 { - // insert a padding - total_padding += needed_padding as usize; - reordered_apps.push(Index { - idx: None, - address: start_address, - size: needed_padding, - }); - - start_address += needed_padding as u64; } - if insert_c { - // insert the c app, also change its address - let c_app = c_apps[order[permutation_index]].as_index(start_address); - if c_app.idx.is_none() { - panic!("C app has no index assigned!"); - } - - reordered_apps.push(c_app); - permutation_index += 1; - } else { - // insert the rust app, don't change its address because it is fixed - let rust_app = - rust_apps[rust_index].as_index(rust_apps[rust_index].candidate_addresses[0]); - if rust_app.idx.is_none() { - panic!("Rust app has no index assigned!"); + // find the configuration that uses the minimum padding + if total_padding < min_padding { + min_padding = total_padding; + saved_configuration = reordered_apps.clone(); + if min_padding == 0 { + break; } - - reordered_apps.push(rust_app); - rust_index += 1; - } - } - - // find the configuration that uses the minimum padding - if total_padding < min_padding { - min_padding = total_padding; - saved_configuration = reordered_apps.clone(); - if min_padding == 0 { - break; } + } else { + break; } } Some(saved_configuration) } -// this return only the binary for a padding +/// This function returns the binary for a padding fn create_padding(size: u32) -> Vec { let mut buf: Vec = Vec::new(); buf.extend_from_slice(&u16::to_le_bytes(2u16)); // tbf version 2 @@ -403,11 +303,12 @@ fn create_padding(size: u32) -> Vec { buf } -// this function takes a rust app's fixed address and aligns it down to ALIGNMENT (1024 currently) +/// This function takes a rust app's fixed address and aligns it down to ALIGNMENT (1024 currently) fn align_down(address: u64) -> u64 { address - address % ALIGNMENT } +/// This function creates the full binary that will be written pub fn create_pkt(configuration: Vec, mut app_binaries: Vec>) -> Vec { let mut pkt: Vec = Vec::new(); for item in configuration.iter() { diff --git a/tockloader-lib/src/tabs/tab.rs b/tockloader-lib/src/tabs/tab.rs index fe69d54a..14d0eabb 100644 --- a/tockloader-lib/src/tabs/tab.rs +++ b/tockloader-lib/src/tabs/tab.rs @@ -75,9 +75,9 @@ impl Tab { } } - pub fn extract_binary(&self, arch: &str) -> Result, TockloaderError> { + pub fn extract_binary(&self, arch: String) -> Result, TockloaderError> { for file in &self.tbf_files { - if file.filename.starts_with(arch) { + if file.filename.starts_with(&arch) { return Ok(file.data.clone()); } } From 26e5438d3b1fb0e6373c26843eda800d74a28b40 Mon Sep 17 00:00:00 2001 From: addrian-77 Date: Fri, 31 Oct 2025 10:35:34 +0200 Subject: [PATCH 6/8] refactor: move settings inside connection Signed-off-by: addrian-77 --- tockloader-cli/src/main.rs | 30 +++++++++--------- tockloader-lib/src/board_settings.rs | 1 + tockloader-lib/src/command_impl/erase_apps.rs | 7 ++--- .../src/command_impl/generalized.rs | 21 ++++--------- tockloader-lib/src/command_impl/info.rs | 8 ++--- tockloader-lib/src/command_impl/install.rs | 25 +++++++-------- tockloader-lib/src/command_impl/list.rs | 8 ++--- tockloader-lib/src/command_impl/probers/io.rs | 14 ++------- tockloader-lib/src/command_impl/serial/io.rs | 15 +++------ tockloader-lib/src/connection.rs | 29 +++++++++++++++-- tockloader-lib/src/lib.rs | 31 ++++--------------- 11 files changed, 82 insertions(+), 107 deletions(-) diff --git a/tockloader-cli/src/main.rs b/tockloader-cli/src/main.rs index 5d5af442..eaee4323 100644 --- a/tockloader-cli/src/main.rs +++ b/tockloader-cli/src/main.rs @@ -111,8 +111,12 @@ async fn open_connection(user_options: &ArgMatches) -> Result Result Result<()> { cli::validate(&mut cmd, sub_matches); let mut conn = open_connection(sub_matches).await?; - let settings = get_board_settings(sub_matches); - let app_details = conn.list(&settings).await.context("Failed to list apps.")?; + let app_details = conn.list().await.context("Failed to list apps.")?; display::print_list(&app_details).await; } Some(("info", sub_matches)) => { cli::validate(&mut cmd, sub_matches); let mut conn = open_connection(sub_matches).await?; - let settings = get_board_settings(sub_matches); let mut attributes = conn - .info(&settings) + .info() .await .context("Failed to get data from the board.")?; @@ -223,20 +229,16 @@ async fn main() -> Result<()> { .context("Failed to use provided tab file.")?; let mut conn = open_connection(sub_matches).await?; - let settings = get_board_settings(sub_matches); - conn.install_app(&settings, tab_file) + conn.install_app(tab_file) .await .context("Failed to install app.")?; } Some(("erase-apps", sub_matches)) => { cli::validate(&mut cmd, sub_matches); let mut conn = open_connection(sub_matches).await?; - let settings = get_board_settings(sub_matches); - conn.erase_apps(&settings) - .await - .context("Failed to erase apps.")?; + conn.erase_apps().await.context("Failed to erase apps.")?; } _ => { println!("Could not run the provided subcommand."); diff --git a/tockloader-lib/src/board_settings.rs b/tockloader-lib/src/board_settings.rs index f9bfec5b..5f119000 100644 --- a/tockloader-lib/src/board_settings.rs +++ b/tockloader-lib/src/board_settings.rs @@ -1,3 +1,4 @@ +#[derive(Clone)] pub struct BoardSettings { pub arch: Option, pub start_address: u64, diff --git a/tockloader-lib/src/command_impl/erase_apps.rs b/tockloader-lib/src/command_impl/erase_apps.rs index fe622fdd..90b88c3c 100644 --- a/tockloader-lib/src/command_impl/erase_apps.rs +++ b/tockloader-lib/src/command_impl/erase_apps.rs @@ -1,14 +1,13 @@ use async_trait::async_trait; -use crate::board_settings::BoardSettings; -use crate::connection::TockloaderConnection; +use crate::connection::{Connection, TockloaderConnection}; use crate::errors::TockloaderError; use crate::{CommandEraseApps, IO}; #[async_trait] impl CommandEraseApps for TockloaderConnection { - async fn erase_apps(&mut self, settings: &BoardSettings) -> Result<(), TockloaderError> { - self.write(settings.start_address, vec![0u8], settings) + async fn erase_apps(&mut self) -> Result<(), TockloaderError> { + self.write(self.get_settings().start_address, vec![0u8]) .await } } diff --git a/tockloader-lib/src/command_impl/generalized.rs b/tockloader-lib/src/command_impl/generalized.rs index 1f2fa901..4638430e 100644 --- a/tockloader-lib/src/command_impl/generalized.rs +++ b/tockloader-lib/src/command_impl/generalized.rs @@ -2,7 +2,6 @@ use async_trait::async_trait; use crate::attributes::app_attributes::AppAttributes; use crate::attributes::system_attributes::SystemAttributes; -use crate::board_settings::BoardSettings; use crate::connection::TockloaderConnection; use crate::errors::TockloaderError; use crate::{IOCommands, IO}; @@ -16,28 +15,20 @@ impl IO for TockloaderConnection { } } - async fn write( - &mut self, - address: u64, - pkt: Vec, - settings: &BoardSettings, - ) -> Result<(), TockloaderError> { + async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError> { match self { - TockloaderConnection::ProbeRS(conn) => conn.write(address, pkt, settings).await, - TockloaderConnection::Serial(conn) => conn.write(address, pkt, settings).await, + TockloaderConnection::ProbeRS(conn) => conn.write(address, pkt).await, + TockloaderConnection::Serial(conn) => conn.write(address, pkt).await, } } } #[async_trait] impl IOCommands for TockloaderConnection { - async fn read_installed_apps( - &mut self, - settings: &BoardSettings, - ) -> Result, TockloaderError> { + async fn read_installed_apps(&mut self) -> Result, TockloaderError> { match self { - TockloaderConnection::ProbeRS(conn) => conn.read_installed_apps(settings).await, - TockloaderConnection::Serial(conn) => conn.read_installed_apps(settings).await, + TockloaderConnection::ProbeRS(conn) => conn.read_installed_apps().await, + TockloaderConnection::Serial(conn) => conn.read_installed_apps().await, } } diff --git a/tockloader-lib/src/command_impl/info.rs b/tockloader-lib/src/command_impl/info.rs index dc502219..c87afdb9 100644 --- a/tockloader-lib/src/command_impl/info.rs +++ b/tockloader-lib/src/command_impl/info.rs @@ -1,18 +1,14 @@ use async_trait::async_trait; use crate::attributes::general_attributes::GeneralAttributes; -use crate::board_settings::BoardSettings; use crate::connection::TockloaderConnection; use crate::errors::TockloaderError; use crate::{CommandInfo, IOCommands}; #[async_trait] impl CommandInfo for TockloaderConnection { - async fn info( - &mut self, - settings: &BoardSettings, - ) -> Result { - let installed_apps = self.read_installed_apps(settings).await.unwrap(); + async fn info(&mut self) -> Result { + let installed_apps = self.read_installed_apps().await.unwrap(); let system_atributes = self.read_system_attributes().await.unwrap(); Ok(GeneralAttributes::new(system_atributes, installed_apps)) } diff --git a/tockloader-lib/src/command_impl/install.rs b/tockloader-lib/src/command_impl/install.rs index 2f95c4f4..593b6538 100644 --- a/tockloader-lib/src/command_impl/install.rs +++ b/tockloader-lib/src/command_impl/install.rs @@ -1,21 +1,17 @@ use async_trait::async_trait; use crate::attributes::app_attributes::AppAttributes; -use crate::board_settings::BoardSettings; use crate::command_impl::reshuffle_apps::{create_pkt, reshuffle_apps, TockApp}; -use crate::connection::TockloaderConnection; +use crate::connection::{Connection, TockloaderConnection}; use crate::errors::TockloaderError; use crate::tabs::tab::Tab; use crate::{CommandInstall, CommandList, IO}; #[async_trait] impl CommandInstall for TockloaderConnection { - async fn install_app( - &mut self, - settings: &BoardSettings, - tab: Tab, - ) -> Result<(), TockloaderError> { - let app_attributes_list: Vec = self.list(settings).await.unwrap(); + async fn install_app(&mut self, tab: Tab) -> Result<(), TockloaderError> { + let settings = self.get_settings(); + let app_attributes_list: Vec = self.list().await.unwrap(); let mut tock_app_list = app_attributes_list .iter() .map(TockApp::from_app_attributes) @@ -24,7 +20,7 @@ impl CommandInstall for TockloaderConnection { // obtain the binaries in a vector let mut app_binaries: Vec> = Vec::new(); - let mut address = settings.start_address; + let mut address = self.get_settings().start_address; for app in app_attributes_list.iter() { app_binaries.push( self.read(address, app.tbf_header.total_size() as usize) @@ -34,20 +30,23 @@ impl CommandInstall for TockloaderConnection { address += app.tbf_header.total_size() as u64; } - let mut app = TockApp::from_tab(&tab, settings).unwrap(); + let mut app = TockApp::from_tab(&tab, &settings).unwrap(); app.replace_idx(tock_app_list.len()); tock_app_list.push(app.clone()); - app_binaries.push(tab.extract_binary(settings.arch.clone().unwrap()).unwrap()); + app_binaries.push( + tab.extract_binary(self.get_settings().arch.clone().unwrap()) + .unwrap(), + ); - let configuration = reshuffle_apps(settings, tock_app_list).unwrap(); + let configuration = reshuffle_apps(&settings, tock_app_list).unwrap(); // create the pkt, this contains all the binaries in a vec let pkt = create_pkt(configuration, app_binaries); // write the pkt - let _ = self.write(settings.start_address, pkt, settings).await; + let _ = self.write(self.get_settings().start_address, pkt).await; Ok(()) } } diff --git a/tockloader-lib/src/command_impl/list.rs b/tockloader-lib/src/command_impl/list.rs index 05097d0b..f024285f 100644 --- a/tockloader-lib/src/command_impl/list.rs +++ b/tockloader-lib/src/command_impl/list.rs @@ -1,17 +1,13 @@ use async_trait::async_trait; use crate::attributes::app_attributes::AppAttributes; -use crate::board_settings::BoardSettings; use crate::connection::TockloaderConnection; use crate::errors::TockloaderError; use crate::{CommandList, IOCommands}; #[async_trait] impl CommandList for TockloaderConnection { - async fn list( - &mut self, - settings: &BoardSettings, - ) -> Result, TockloaderError> { - self.read_installed_apps(settings).await + async fn list(&mut self) -> Result, TockloaderError> { + self.read_installed_apps().await } } diff --git a/tockloader-lib/src/command_impl/probers/io.rs b/tockloader-lib/src/command_impl/probers/io.rs index 09105773..b2aa445b 100644 --- a/tockloader-lib/src/command_impl/probers/io.rs +++ b/tockloader-lib/src/command_impl/probers/io.rs @@ -3,7 +3,6 @@ use probe_rs::{flashing::DownloadOptions, MemoryInterface}; use crate::{ attributes::{app_attributes::AppAttributes, system_attributes::SystemAttributes}, - board_settings::BoardSettings, connection::{Connection, ProbeRSConnection}, errors::{InternalError, TockloaderError}, IOCommands, IO, @@ -23,12 +22,7 @@ impl IO for ProbeRSConnection { Ok(appdata) } - async fn write( - &mut self, - address: u64, - pkt: Vec, - _settings: &BoardSettings, - ) -> Result<(), TockloaderError> { + async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError> { if !self.is_open() { return Err(InternalError::ConnectionNotOpen.into()); } @@ -47,13 +41,11 @@ impl IO for ProbeRSConnection { #[async_trait] impl IOCommands for ProbeRSConnection { - async fn read_installed_apps( - &mut self, - settings: &BoardSettings, - ) -> Result, TockloaderError> { + async fn read_installed_apps(&mut self) -> Result, TockloaderError> { if !self.is_open() { return Err(InternalError::ConnectionNotOpen.into()); } + let settings = self.get_settings(); let session = self.session.as_mut().expect("Board must be open"); let mut core = session.core(self.target_info.core)?; diff --git a/tockloader-lib/src/command_impl/serial/io.rs b/tockloader-lib/src/command_impl/serial/io.rs index 0efbee7a..9d8af783 100644 --- a/tockloader-lib/src/command_impl/serial/io.rs +++ b/tockloader-lib/src/command_impl/serial/io.rs @@ -2,7 +2,6 @@ use async_trait::async_trait; use crate::{ attributes::{app_attributes::AppAttributes, system_attributes::SystemAttributes}, - board_settings::BoardSettings, bootloader_serial::{issue_command, ping_bootloader_and_wait_for_response, Command, Response}, connection::{Connection, SerialConnection}, errors::{InternalError, TockloaderError}, @@ -37,12 +36,8 @@ impl IO for SerialConnection { Ok(appdata) } - async fn write( - &mut self, - address: u64, - pkt: Vec, - settings: &BoardSettings, - ) -> Result<(), TockloaderError> { + async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError> { + let settings = self.get_settings(); let stream = self.stream.as_mut().expect("Board must be open"); let mut binary = pkt.clone(); @@ -77,13 +72,11 @@ impl IO for SerialConnection { #[async_trait] impl IOCommands for SerialConnection { - async fn read_installed_apps( - &mut self, - settings: &BoardSettings, - ) -> Result, TockloaderError> { + async fn read_installed_apps(&mut self) -> Result, TockloaderError> { if !self.is_open() { return Err(InternalError::ConnectionNotOpen.into()); } + let settings = self.get_settings(); let stream = self.stream.as_mut().expect("Board must be open"); ping_bootloader_and_wait_for_response(stream).await?; diff --git a/tockloader-lib/src/connection.rs b/tockloader-lib/src/connection.rs index 7d0c6939..5ae56c82 100644 --- a/tockloader-lib/src/connection.rs +++ b/tockloader-lib/src/connection.rs @@ -6,6 +6,7 @@ use probe_rs::{Permissions, Session}; use tokio::io::AsyncWriteExt; use tokio_serial::{FlowControl, Parity, SerialPort, SerialStream, StopBits}; +use crate::board_settings::BoardSettings; use crate::errors::TockloaderError; use log::info; pub struct ProbeTargetInfo { @@ -51,6 +52,7 @@ pub trait Connection { /// `open` or any other method is undefined behavior. async fn close(&mut self) -> Result<(), TockloaderError>; fn is_open(&self) -> bool; + fn get_settings(&self) -> BoardSettings; } pub struct ProbeRSConnection { @@ -60,14 +62,20 @@ pub struct ProbeRSConnection { pub(crate) target_info: ProbeTargetInfo, /// Only used for opening a new connection debug_probe: DebugProbeInfo, + pub settings: BoardSettings, } impl ProbeRSConnection { - pub fn new(debug_probe: DebugProbeInfo, target_info: ProbeTargetInfo) -> Self { + pub fn new( + debug_probe: DebugProbeInfo, + target_info: ProbeTargetInfo, + settings: BoardSettings, + ) -> Self { Self { session: None, target_info, debug_probe, + settings, } } } @@ -93,6 +101,10 @@ impl Connection for ProbeRSConnection { fn is_open(&self) -> bool { self.session.is_some() } + + fn get_settings(&self) -> BoardSettings { + self.settings.clone() + } } pub struct SerialConnection { @@ -102,14 +114,16 @@ pub struct SerialConnection { pub(crate) target_info: SerialTargetInfo, /// Path to the serial port. This is only used for opening a new connection. port: String, + pub settings: BoardSettings, } impl SerialConnection { - pub fn new(port: String, target_info: SerialTargetInfo) -> Self { + pub fn new(port: String, target_info: SerialTargetInfo, settings: BoardSettings) -> Self { Self { stream: None, target_info, port, + settings, } } pub fn into_inner_stream(self) -> Option { @@ -150,6 +164,10 @@ impl Connection for SerialConnection { fn is_open(&self) -> bool { self.stream.is_some() } + + fn get_settings(&self) -> BoardSettings { + self.settings.clone() + } } /// This is an utility enum to make your life easier when you want to abstract @@ -194,4 +212,11 @@ impl Connection for TockloaderConnection { TockloaderConnection::Serial(conn) => conn.is_open(), } } + + fn get_settings(&self) -> BoardSettings { + match self { + TockloaderConnection::ProbeRS(conn) => conn.get_settings(), + TockloaderConnection::Serial(conn) => conn.get_settings(), + } + } } diff --git a/tockloader-lib/src/lib.rs b/tockloader-lib/src/lib.rs index 22167472..287c246b 100644 --- a/tockloader-lib/src/lib.rs +++ b/tockloader-lib/src/lib.rs @@ -18,7 +18,6 @@ use tokio_serial::SerialPortInfo; use crate::attributes::app_attributes::AppAttributes; use crate::attributes::general_attributes::GeneralAttributes; use crate::attributes::system_attributes::SystemAttributes; -use crate::board_settings::BoardSettings; use crate::errors::*; use crate::tabs::tab::Tab; @@ -38,52 +37,34 @@ pub fn list_serial_ports() -> Result, TockloaderError> { #[async_trait] pub trait CommandList { - async fn list( - &mut self, - settings: &BoardSettings, - ) -> Result, TockloaderError>; + async fn list(&mut self) -> Result, TockloaderError>; } #[async_trait] pub trait CommandInfo { - async fn info( - &mut self, - settings: &BoardSettings, - ) -> Result; + async fn info(&mut self) -> Result; } #[async_trait] pub trait CommandInstall { - async fn install_app( - &mut self, - settings: &BoardSettings, - tab_file: Tab, - ) -> Result<(), TockloaderError>; + async fn install_app(&mut self, tab_file: Tab) -> Result<(), TockloaderError>; } #[async_trait] pub trait CommandEraseApps { - async fn erase_apps(&mut self, settings: &BoardSettings) -> Result<(), TockloaderError>; + async fn erase_apps(&mut self) -> Result<(), TockloaderError>; } #[async_trait] pub trait IO: Send { async fn read(&mut self, address: u64, size: usize) -> Result, TockloaderError>; - async fn write( - &mut self, - address: u64, - pkt: Vec, - settings: &BoardSettings, - ) -> Result<(), TockloaderError>; + async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError>; } #[async_trait] pub trait IOCommands: Send { - async fn read_installed_apps( - &mut self, - settings: &BoardSettings, - ) -> Result, TockloaderError>; + async fn read_installed_apps(&mut self) -> Result, TockloaderError>; async fn read_system_attributes(&mut self) -> Result; } From 42fbaf86546c0fc9b128dc7bce8745f569d0fb5b Mon Sep 17 00:00:00 2001 From: addrian-77 Date: Fri, 31 Oct 2025 11:37:06 +0200 Subject: [PATCH 7/8] refactor: change vec to slice inside write function Signed-off-by: addrian-77 --- tockloader-lib/src/command_impl/erase_apps.rs | 2 +- tockloader-lib/src/command_impl/generalized.rs | 2 +- tockloader-lib/src/command_impl/install.rs | 6 +++--- tockloader-lib/src/command_impl/probers/io.rs | 4 ++-- tockloader-lib/src/command_impl/serial/io.rs | 4 ++-- tockloader-lib/src/lib.rs | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tockloader-lib/src/command_impl/erase_apps.rs b/tockloader-lib/src/command_impl/erase_apps.rs index 90b88c3c..db676a20 100644 --- a/tockloader-lib/src/command_impl/erase_apps.rs +++ b/tockloader-lib/src/command_impl/erase_apps.rs @@ -7,7 +7,7 @@ use crate::{CommandEraseApps, IO}; #[async_trait] impl CommandEraseApps for TockloaderConnection { async fn erase_apps(&mut self) -> Result<(), TockloaderError> { - self.write(self.get_settings().start_address, vec![0u8]) + self.write(self.get_settings().start_address, &[0u8]) .await } } diff --git a/tockloader-lib/src/command_impl/generalized.rs b/tockloader-lib/src/command_impl/generalized.rs index 4638430e..f0d14601 100644 --- a/tockloader-lib/src/command_impl/generalized.rs +++ b/tockloader-lib/src/command_impl/generalized.rs @@ -15,7 +15,7 @@ impl IO for TockloaderConnection { } } - async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError> { + async fn write(&mut self, address: u64, pkt: &[u8]) -> Result<(), TockloaderError> { match self { TockloaderConnection::ProbeRS(conn) => conn.write(address, pkt).await, TockloaderConnection::Serial(conn) => conn.write(address, pkt).await, diff --git a/tockloader-lib/src/command_impl/install.rs b/tockloader-lib/src/command_impl/install.rs index 593b6538..6beb49d9 100644 --- a/tockloader-lib/src/command_impl/install.rs +++ b/tockloader-lib/src/command_impl/install.rs @@ -20,7 +20,7 @@ impl CommandInstall for TockloaderConnection { // obtain the binaries in a vector let mut app_binaries: Vec> = Vec::new(); - let mut address = self.get_settings().start_address; + let mut address = settings.start_address; for app in app_attributes_list.iter() { app_binaries.push( self.read(address, app.tbf_header.total_size() as usize) @@ -36,7 +36,7 @@ impl CommandInstall for TockloaderConnection { tock_app_list.push(app.clone()); app_binaries.push( - tab.extract_binary(self.get_settings().arch.clone().unwrap()) + tab.extract_binary(settings.arch.clone().unwrap()) .unwrap(), ); @@ -46,7 +46,7 @@ impl CommandInstall for TockloaderConnection { let pkt = create_pkt(configuration, app_binaries); // write the pkt - let _ = self.write(self.get_settings().start_address, pkt).await; + let _ = self.write(settings.start_address, &pkt).await; Ok(()) } } diff --git a/tockloader-lib/src/command_impl/probers/io.rs b/tockloader-lib/src/command_impl/probers/io.rs index b2aa445b..046f145a 100644 --- a/tockloader-lib/src/command_impl/probers/io.rs +++ b/tockloader-lib/src/command_impl/probers/io.rs @@ -22,14 +22,14 @@ impl IO for ProbeRSConnection { Ok(appdata) } - async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError> { + async fn write(&mut self, address: u64, pkt: &[u8]) -> Result<(), TockloaderError> { if !self.is_open() { return Err(InternalError::ConnectionNotOpen.into()); } let session = self.session.as_mut().expect("Board must be open"); let mut loader = session.target().flash_loader(); - loader.add_data(address, &pkt)?; + loader.add_data(address, pkt)?; let mut options = DownloadOptions::default(); options.keep_unwritten_bytes = true; diff --git a/tockloader-lib/src/command_impl/serial/io.rs b/tockloader-lib/src/command_impl/serial/io.rs index 9d8af783..618adfad 100644 --- a/tockloader-lib/src/command_impl/serial/io.rs +++ b/tockloader-lib/src/command_impl/serial/io.rs @@ -36,10 +36,10 @@ impl IO for SerialConnection { Ok(appdata) } - async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError> { + async fn write(&mut self, address: u64, pkt: &[u8]) -> Result<(), TockloaderError> { let settings = self.get_settings(); let stream = self.stream.as_mut().expect("Board must be open"); - let mut binary = pkt.clone(); + let mut binary = pkt.to_vec(); if !binary.len().is_multiple_of(settings.page_size as usize) { binary.extend(vec![ diff --git a/tockloader-lib/src/lib.rs b/tockloader-lib/src/lib.rs index 287c246b..fd023eef 100644 --- a/tockloader-lib/src/lib.rs +++ b/tockloader-lib/src/lib.rs @@ -59,7 +59,7 @@ pub trait CommandEraseApps { pub trait IO: Send { async fn read(&mut self, address: u64, size: usize) -> Result, TockloaderError>; - async fn write(&mut self, address: u64, pkt: Vec) -> Result<(), TockloaderError>; + async fn write(&mut self, address: u64, pkt: &[u8]) -> Result<(), TockloaderError>; } #[async_trait] From 8b608af6ed84b792ddc37691cba34f769286ccc4 Mon Sep 17 00:00:00 2001 From: Adrian Lungu Date: Sun, 14 Dec 2025 16:05:48 +0200 Subject: [PATCH 8/8] wip Signed-off-by: Adrian Lungu --- tockloader-lib/src/board_settings.rs | 2 + tockloader-lib/src/command_impl/install.rs | 5 +- .../src/command_impl/reshuffle_apps.rs | 11 ++-- tockloader-lib/src/known_boards.rs | 2 + tockloader-lib/src/tabs/tab.rs | 59 ++++++++++++++++++- 5 files changed, 68 insertions(+), 11 deletions(-) diff --git a/tockloader-lib/src/board_settings.rs b/tockloader-lib/src/board_settings.rs index 5f119000..22be548b 100644 --- a/tockloader-lib/src/board_settings.rs +++ b/tockloader-lib/src/board_settings.rs @@ -3,6 +3,7 @@ pub struct BoardSettings { pub arch: Option, pub start_address: u64, pub page_size: u64, + pub ram_start_address: u64, } // TODO(george-cosma): Does a default implementation make sense for this? Is a @@ -13,6 +14,7 @@ impl Default for BoardSettings { arch: None, start_address: 0x30000, page_size: 512, + ram_start_address: 0x20000000, } } } diff --git a/tockloader-lib/src/command_impl/install.rs b/tockloader-lib/src/command_impl/install.rs index 6beb49d9..2587190b 100644 --- a/tockloader-lib/src/command_impl/install.rs +++ b/tockloader-lib/src/command_impl/install.rs @@ -35,10 +35,7 @@ impl CommandInstall for TockloaderConnection { app.replace_idx(tock_app_list.len()); tock_app_list.push(app.clone()); - app_binaries.push( - tab.extract_binary(settings.arch.clone().unwrap()) - .unwrap(), - ); + app_binaries.push(tab.extract_binary(settings.arch.clone().unwrap()).unwrap()); let configuration = reshuffle_apps(&settings, tock_app_list).unwrap(); diff --git a/tockloader-lib/src/command_impl/reshuffle_apps.rs b/tockloader-lib/src/command_impl/reshuffle_apps.rs index 2d3dfe67..cce5db4a 100644 --- a/tockloader-lib/src/command_impl/reshuffle_apps.rs +++ b/tockloader-lib/src/command_impl/reshuffle_apps.rs @@ -3,7 +3,7 @@ use log::warn; use crate::attributes::app_attributes::AppAttributes; use crate::board_settings::BoardSettings; -use crate::tabs::tab::Tab; +use crate::tabs::tab::{Tab, TbfFile}; use tbf_parser::parse::{parse_tbf_header, parse_tbf_header_lengths}; const ALIGNMENT: u64 = 1024; @@ -23,7 +23,7 @@ pub struct FlexibleApp { #[derive(Clone)] pub struct FixedApp { idx: Option, - candidate_addresses: Vec, + compatible_binaries: Vec<(Vec, u64, u64)>, size: u64, } @@ -35,11 +35,14 @@ impl TockApp { } } - pub fn from_app_attributes(app_attributes: &AppAttributes) -> TockApp { + pub fn from_app_attributes( + app_attributes: &AppAttributes, + settings: &BoardSettings, + ) -> TockApp { if let Some(address) = app_attributes.tbf_header.get_fixed_address_flash() { TockApp::Fixed(FixedApp { idx: None, - candidate_addresses: vec![address as u64], // (adi): change this when tbf selector gets merged + compatible_binaries: vec![vec![0u8], address, settings.ram_start_address], // (adi): change this when tbf selector gets merged size: app_attributes.tbf_header.total_size() as u64, }) } else { diff --git a/tockloader-lib/src/known_boards.rs b/tockloader-lib/src/known_boards.rs index 6a986273..009992de 100644 --- a/tockloader-lib/src/known_boards.rs +++ b/tockloader-lib/src/known_boards.rs @@ -26,6 +26,7 @@ impl KnownBoard for NucleoF4 { arch: Some("cortex-m4".to_string()), start_address: 0x08040000, page_size: 512, + ram_start_address: 0x20000000, } } } @@ -49,6 +50,7 @@ impl KnownBoard for MicrobitV2 { arch: Some("cortex-m4".to_string()), start_address: 0x00040000, page_size: 512, + ram_start_address: 0x20000000, } } } diff --git a/tockloader-lib/src/tabs/tab.rs b/tockloader-lib/src/tabs/tab.rs index 14d0eabb..e5729586 100644 --- a/tockloader-lib/src/tabs/tab.rs +++ b/tockloader-lib/src/tabs/tab.rs @@ -2,13 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // Copyright OXIDOS AUTOMOTIVE 2024. +use crate::board_settings::BoardSettings; use crate::errors::{TabError, TockloaderError}; use crate::tabs::metadata::Metadata; use std::fs::File; use std::io::Read; use tar::Archive; -struct TbfFile { +pub struct TbfFile { pub filename: String, pub data: Vec, } @@ -75,9 +76,61 @@ impl Tab { } } - pub fn extract_binary(&self, arch: String) -> Result, TockloaderError> { + /// This function returns all the compatible binaries for a given tab + /// + /// Vector components: + /// - binary (Vec) + /// - start_address: u64 + /// - ram_start_address: u64 + pub fn filter_tbfs( + &self, + settings: &BoardSettings, + ) -> Result, u64, u64)>, TockloaderError> { + // save the file + // also save flash start and ram start for comparing easily later + let mut compatible_tbfs: Vec<(Vec, u64, u64)> = Vec::new(); for file in &self.tbf_files { - if file.filename.starts_with(&arch) { + let (arch, flash, ram) = Self::split_arch(file.filename.to_string()); + // check if we have the same arch + // check if flash and ram fit + if flash != 0 && ram != 0 { + if arch.starts_with(settings.arch.as_ref().unwrap()) + && flash >= settings.start_address + && ram >= settings.ram_start_address + { + compatible_tbfs.push((file.data.clone(), flash, ram)); + } + } else if arch.starts_with(settings.arch.as_ref().unwrap()) { + // this happens for C apps, we'll have + // arch = "cortex-m4.tbf" + // without any flash and ram values + compatible_tbfs.push((file.data.clone(), flash, ram)); + } + } + Ok(compatible_tbfs) + } + + fn split_arch(filename: String) -> (String, u64, u64) { + // filename is always formatted like this: + // "cortex-m0.0x10020000.0x20004000.tab" + // splitting by .0x will give us "arch", "flash start", "ram start.tab" + // 3 items + log::info!("filename {filename}"); + let data: Vec<&str> = filename.split(".0x").collect(); + if data.len() == 3 { + let flashaddr: u64 = u64::from_str_radix(data[1], 16).unwrap(); + // split the ram address again because it also contains .tab + // take the first item of the tuple + let ramaddr: u64 = u64::from_str_radix(data[2].split_once(".").unwrap().0, 16).unwrap(); + (data[0].to_string(), flashaddr, ramaddr) + } else { + (data[0].to_string(), 0, 0) + } + } + + pub fn extract_binary(&self, arch: &str) -> Result, TockloaderError> { + for file in &self.tbf_files { + if file.filename.starts_with(arch) { return Ok(file.data.clone()); } }