From 022dafa03191c20d2e86beb0de024720fdd0a040 Mon Sep 17 00:00:00 2001 From: K-Nicolas-10 Date: Fri, 17 Jul 2026 00:25:38 +0300 Subject: [PATCH] feat: add start_address and change system_atributes to conform --- tockloader-lib/src/attributes/decode.rs | 30 ++-- .../src/attributes/system_attributes.rs | 151 +++++++++++------- tockloader-lib/src/board_settings.rs | 2 + tockloader-lib/src/bootloader_serial.rs | 1 - tockloader-lib/src/command_impl/info.rs | 4 +- tockloader-lib/src/command_impl/probers/io.rs | 5 +- .../src/command_impl/reshuffle_apps.rs | 4 + tockloader-lib/src/command_impl/serial/io.rs | 5 +- tockloader-lib/src/known_boards.rs | 6 +- 9 files changed, 124 insertions(+), 84 deletions(-) diff --git a/tockloader-lib/src/attributes/decode.rs b/tockloader-lib/src/attributes/decode.rs index 229da57d..720c9744 100644 --- a/tockloader-lib/src/attributes/decode.rs +++ b/tockloader-lib/src/attributes/decode.rs @@ -41,7 +41,11 @@ pub(crate) fn decode_attribute(step: &[u8]) -> Option { let mut key = String::new(); for n in decoder_key { - key.push(n.expect("Error getting key for attributes.")); + // TODO: This should not expect, we should return none in case the chip doesn't have a bootloader + match n { + Ok(c) => key.push(c), + Err(_) => return None, + } } key = key.trim_end_matches('\0').to_string(); @@ -56,27 +60,13 @@ pub(crate) fn decode_attribute(step: &[u8]) -> Option { let mut value = String::new(); for n in decoder_value { - value.push(n.expect("Error getting key for attributes.")); + // TODO: This should not expect, we should return none in case the chip doesn't have a bootloader + match n { + Ok(c) => value.push(c), + Err(_) => return None, + } } value = value.trim_end_matches('\0').to_string(); Some(DecodedAttribute::new(key, value)) } - -// TODO(eva-cosma) replace this function with std::str::from_utf8(...). It -// does the same thing. - -/// Transform a byte-slice into a String. -/// -/// # Panics -/// -/// This code panics if the given bytes are not utf-8 representable -pub(crate) fn bytes_to_string(raw: &[u8]) -> String { - let decoder = utf8_decode::Decoder::new(raw.iter().cloned()); - - let mut string = String::new(); - for n in decoder { - string.push(n.expect("Error getting key for attributes.")); - } - string -} diff --git a/tockloader-lib/src/attributes/system_attributes.rs b/tockloader-lib/src/attributes/system_attributes.rs index 62a2249f..48abfb2e 100644 --- a/tockloader-lib/src/attributes/system_attributes.rs +++ b/tockloader-lib/src/attributes/system_attributes.rs @@ -7,7 +7,7 @@ use byteorder::{ByteOrder, LittleEndian}; use crate::errors::{AttributeParseError, TockError, TockloaderError}; use crate::IO; -use super::decode::{bytes_to_string, decode_attribute}; +use super::decode::decode_attribute; /// This structure contains all relevant information about board that is stored /// in the bootloader ROM. @@ -48,12 +48,28 @@ impl SystemAttributes { } } - /// Read system attributes using a generalized connection. A bootloader must be - /// present on this board for this function to work properly. - /// + /// Check if the bootloader is present on the version of Tock + pub(crate) async fn bootloader_is_present( + conn: &mut dyn IO, + flash_address: u64, + ) -> Result { + // If a bootloader is present, the start of 0x400 will read + // "TOCKBOOTLOADER", which is exactly 14 bytes long, so we'll read + // 14 bytes starting from 0x400. + let flag = conn.read(flash_address + 0x400, 14).await?; + if flag == "TOCKBOOTLOADER".as_bytes() { + Ok(true) + } else { + Ok(false) + } + } + /// Read system and kernel attributes. + /// System attributes are read only if the board has + /// a bootloader present. /// # Parameters - /// - `conn` : Either a SerialConnection or a ProbeRSConnection - /// + /// - `conn` : Either a SerialConnection or a ProbeRSConnection. + /// - `flash_address` : The start of flash memory. + /// - `start_address` : The start of User Space Applications in flash memory. /// # Returns /// - Ok(result): if attributes were read successfully /// - Err(TockloaderError::MisconfiguredBoard): if no start address is found or valid @@ -62,74 +78,91 @@ impl SystemAttributes { /// - Err(TockloaderError::SerialReadError): if reading fails on Serial pub(crate) async fn read_system_attributes( conn: &mut dyn IO, + flash_address: u64, + start_address: u64, ) -> Result { let mut result = SystemAttributes::new(); // System attributes start at 0x600 and up to 0x9FF. See: // https://book.tockos.org/doc/memory_layout#flash-1 - let address = 0x600; + let address = flash_address + 0x600; let buf = conn.read(address, 64 * 16).await?; let mut data = buf.chunks(64); - for current_slot in 0..data.len() { - let slot_data = match data.next() { - Some(data) => data, - None => break, - }; - - // If the attribute chunk was successfully decoded, assign its value - // to the corresponding field in `result` based on the index: - // - 0 = board name, - // - 1 = architecture, - // - 2 = application start address (parsed from hex string), - // - 3 = boot hash, _ = invalid or missing data is skipped. - // NOTE: this can also be done by looping directly through the key attributes. - if let Some(decoded_attributes) = decode_attribute(slot_data) { - match current_slot { - 0 => { - result.board = Some(decoded_attributes.value.to_string()); - } - 1 => { - result.arch = Some(decoded_attributes.value.to_string()); - } - 2 => { - // Parse hex string like "0x40000" into actual u64 value - result.appaddr = Some( - u64::from_str_radix( - decoded_attributes - .value - .to_string() - .trim_start_matches("0x"), - 16, - ) - .map_err(|e| { - TockError::AttributeParsing(AttributeParseError::InvalidNumber(e)) - })?, - ); - } - 3 => { - result.boothash = Some(decoded_attributes.value.to_string()); + let has_bootloader = Self::bootloader_is_present(conn, flash_address) + .await + .unwrap_or(false); + + if !has_bootloader { + // TODO: Chain: CLI arguments -> hardcoded start_address -> calculate from kernel app start address + result.appaddr = Some(start_address); + } else { + for current_slot in 0..data.len() { + let slot_data = match data.next() { + Some(data) => data, + None => break, + }; + + // If the attribute chunk was successfully decoded, assign its value + // to the corresponding field in `result` based on the index: + // - 0 = board name, + // - 1 = architecture, + // - 2 = application start address (parsed from hex string), + // - 3 = boot hash, _ = invalid or missing data is skipped. + // NOTE: this can also be done by looping directly through the key attributes. + if let Some(decoded_attributes) = decode_attribute(slot_data) { + match current_slot { + 0 => { + result.board = Some(decoded_attributes.value.to_string()); + } + 1 => { + result.arch = Some(decoded_attributes.value.to_string()); + } + 2 => { + // Parse hex string like "0x40000" into actual u64 value + result.appaddr = Some( + u64::from_str_radix( + decoded_attributes + .value + .to_string() + .trim_start_matches("0x"), + 16, + ) + .map_err(|e| { + TockError::AttributeParsing(AttributeParseError::InvalidNumber( + e, + )) + })?, + ); + } + 3 => { + result.boothash = Some(decoded_attributes.value.to_string()); + } + _ => {} } - _ => {} + } else { + continue; } - } else { - continue; } - } - - // TODO(eva-cosma): separate kernel attributes from kernel flags. - let address = 0x40E; + // At this address lives the version of the bootloader we are using. + // Trying to read this when no bootloader is present will result in a panic. + // So we first verify if we have a bootloader, if not we just skip this and + // return None for the bootloader version. + let address = flash_address + 0x40E; - let buf = conn.read(address, 8).await?; + let buf = conn.read(address, 8).await?; - let string = String::from_utf8(buf.to_vec()) - .map_err(|e| TockError::AttributeParsing(AttributeParseError::InvalidString(e)))?; + let string = String::from_utf8(buf.to_vec()) + .map_err(|e| TockError::AttributeParsing(AttributeParseError::InvalidString(e)))?; - let string = string.trim_matches(char::from(0)); + let string = string.trim_matches(char::from(0)); - result.bootloader_version = Some(string.to_owned()); + result.bootloader_version = Some(string.to_owned()); + } + + // TODO(eva-cosma): separate kernel attributes from kernel flags. // The 100 bytes prior to the application start address are reserved for the kernel attributes and flags let kernel_attr_addr = result @@ -138,7 +171,9 @@ impl SystemAttributes { - 100; let kernel_attr_binary = conn.read(kernel_attr_addr, 100).await?; - let sentinel = bytes_to_string(&kernel_attr_binary[96..100]); + let sentinel = std::str::from_utf8(&kernel_attr_binary[96..100]) + .unwrap_or("unknown") + .to_string(); let kernel_version = LittleEndian::read_uint(&kernel_attr_binary[95..96], 1); let app_memory_len = LittleEndian::read_u32(&kernel_attr_binary[84..92]); diff --git a/tockloader-lib/src/board_settings.rs b/tockloader-lib/src/board_settings.rs index 7fc81234..70725fd2 100644 --- a/tockloader-lib/src/board_settings.rs +++ b/tockloader-lib/src/board_settings.rs @@ -1,6 +1,7 @@ #[derive(Clone)] pub struct BoardSettings { pub arch: Option, + pub flash_address: u64, pub start_address: u64, pub page_size: u64, pub ram_start_address: u64, @@ -12,6 +13,7 @@ impl Default for BoardSettings { fn default() -> Self { Self { arch: None, + flash_address: 0x00000, // this would be actually like -0x10000 start_address: 0x30000, page_size: 512, ram_start_address: 0x20000000, diff --git a/tockloader-lib/src/bootloader_serial.rs b/tockloader-lib/src/bootloader_serial.rs index 24ec8d05..03ccb626 100644 --- a/tockloader-lib/src/bootloader_serial.rs +++ b/tockloader-lib/src/bootloader_serial.rs @@ -156,7 +156,6 @@ async fn write_bytes( .map_err(|_| TockError::BootloaderTimeout)? } -#[allow(dead_code)] pub async fn ping_bootloader_and_wait_for_response( port: &mut SerialStream, ) -> Result<(), TockloaderError> { diff --git a/tockloader-lib/src/command_impl/info.rs b/tockloader-lib/src/command_impl/info.rs index c87afdb9..2c29daef 100644 --- a/tockloader-lib/src/command_impl/info.rs +++ b/tockloader-lib/src/command_impl/info.rs @@ -8,8 +8,8 @@ use crate::{CommandInfo, IOCommands}; #[async_trait] impl CommandInfo for TockloaderConnection { async fn info(&mut self) -> Result { - let installed_apps = self.read_installed_apps().await.unwrap(); - let system_atributes = self.read_system_attributes().await.unwrap(); + let installed_apps = self.read_installed_apps().await?; + let system_atributes = self.read_system_attributes().await?; Ok(GeneralAttributes::new(system_atributes, installed_apps)) } } diff --git a/tockloader-lib/src/command_impl/probers/io.rs b/tockloader-lib/src/command_impl/probers/io.rs index 886c1ab8..4bca6c5a 100644 --- a/tockloader-lib/src/command_impl/probers/io.rs +++ b/tockloader-lib/src/command_impl/probers/io.rs @@ -53,7 +53,10 @@ impl IOCommands for ProbeRSConnection { if !self.is_open() { return Err(InternalError::ConnectionNotOpen.into()); } - let system_attributes = SystemAttributes::read_system_attributes(self).await?; + let flash_address = self.get_settings().flash_address; + let start_address = self.get_settings().start_address; + let system_attributes = + SystemAttributes::read_system_attributes(self, flash_address, start_address).await?; Ok(system_attributes) } } diff --git a/tockloader-lib/src/command_impl/reshuffle_apps.rs b/tockloader-lib/src/command_impl/reshuffle_apps.rs index b0fec8cf..28e8a382 100644 --- a/tockloader-lib/src/command_impl/reshuffle_apps.rs +++ b/tockloader-lib/src/command_impl/reshuffle_apps.rs @@ -511,6 +511,7 @@ mod tests { fn install_new_c_app() { let settings: &BoardSettings = &BoardSettings { arch: Some("cortex-m4".to_string()), + flash_address: 0x00000000, start_address: 0x00040000, page_size: 512, ram_start_address: 0x20000000, @@ -568,6 +569,7 @@ mod tests { fn install_new_rust_app() { let settings: &BoardSettings = &BoardSettings { arch: Some("cortex-m4".to_string()), + flash_address: 0x00000000, start_address: 0x00040000, page_size: 512, ram_start_address: 0x20000000, @@ -604,6 +606,7 @@ mod tests { fn install_more_rust_apps() { let settings: &BoardSettings = &BoardSettings { arch: Some("cortex-m4".to_string()), + flash_address: 0x00000000, start_address: 0x00040000, page_size: 512, ram_start_address: 0x20000000, @@ -678,6 +681,7 @@ mod tests { fn insert_c_app_between_rust_apps() { let settings: &BoardSettings = &BoardSettings { arch: Some("cortex-m4".to_string()), + flash_address: 0x00000000, start_address: 0x00040000, page_size: 512, ram_start_address: 0x20000000, diff --git a/tockloader-lib/src/command_impl/serial/io.rs b/tockloader-lib/src/command_impl/serial/io.rs index 2f0a8c69..fba34e74 100644 --- a/tockloader-lib/src/command_impl/serial/io.rs +++ b/tockloader-lib/src/command_impl/serial/io.rs @@ -84,7 +84,10 @@ impl IOCommands for SerialConnection { ping_bootloader_and_wait_for_response(stream).await?; - let system_attributes = SystemAttributes::read_system_attributes(self).await?; + let flash_address = self.get_settings().flash_address; + let start_address = self.get_settings().start_address; + let system_attributes = + SystemAttributes::read_system_attributes(self, flash_address, start_address).await?; Ok(system_attributes) } } diff --git a/tockloader-lib/src/known_boards.rs b/tockloader-lib/src/known_boards.rs index be819dfd..ce9f29a8 100644 --- a/tockloader-lib/src/known_boards.rs +++ b/tockloader-lib/src/known_boards.rs @@ -24,6 +24,7 @@ impl KnownBoard for NucleoF4 { fn get_settings(&self) -> BoardSettings { BoardSettings { arch: Some("cortex-m4".to_string()), + flash_address: 0x08000000, start_address: 0x08040000, page_size: 512, ram_start_address: 0x20000000, @@ -48,6 +49,7 @@ impl KnownBoard for MicrobitV2 { fn get_settings(&self) -> BoardSettings { BoardSettings { arch: Some("cortex-m4".to_string()), + flash_address: 0x00000000, start_address: 0x00040000, page_size: 512, ram_start_address: 0x20000000, @@ -72,6 +74,7 @@ impl KnownBoard for Nrf52840dk { fn get_settings(&self) -> BoardSettings { BoardSettings { arch: Some("cortex-m4".to_string()), + flash_address: 0x00000000, start_address: 0x00040000, page_size: 4096, ram_start_address: 0x20008000, @@ -96,7 +99,8 @@ impl KnownBoard for NucleoU545ReQ { fn get_settings(&self) -> BoardSettings { BoardSettings { arch: Some("cortex-m4".to_string()), - start_address: 0x08000000, + flash_address: 0x08000000, + start_address: 0x08040000, page_size: 8192, ram_start_address: 0x20000000, }