Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 10 additions & 20 deletions tockloader-lib/src/attributes/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ pub(crate) fn decode_attribute(step: &[u8]) -> Option<DecodedAttribute> {

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,
}
Comment on lines +44 to +48

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are changing this, can we change it directly to the std implementation?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utf8_decode should be eradicated from this codebase

}

key = key.trim_end_matches('\0').to_string();
Expand All @@ -56,27 +60,13 @@ pub(crate) fn decode_attribute(step: &[u8]) -> Option<DecodedAttribute> {
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
}
151 changes: 93 additions & 58 deletions tockloader-lib/src/attributes/system_attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
flash_address: u64,
flash_start_address: u64,

) -> Result<bool, TockloaderError> {
// 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)
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
}

Add a space after

/// 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
Expand All @@ -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,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
flash_address: u64,
flash_start_address: u64,

start_address: u64,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
start_address: u64,
app_start_address: u64,

) -> Result<SystemAttributes, TockloaderError> {
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;
Comment on lines 67 to +87

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continue this comment. It start at an offset of 0x600, an example being visible at . Furthermore ,this is also evidenced by the linker script in tock upstream


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);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bubble up the error, don't unwrap_or


if !has_bootloader {
// TODO: Chain: CLI arguments -> hardcoded start_address -> calculate from kernel app start address

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// TODO: Chain: CLI arguments -> hardcoded start_address -> calculate from kernel app start address
// TODO(K-Nicolas-10): 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() {
Comment on lines +100 to +101

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} else {
for current_slot in 0..data.len() {
} else {
// TODO(eva-cosma): Clean this up/refactor
for current_slot in 0..data.len() {

You can also do the cleanup in this PR, but you will need to test the new implementation. We can discuss what I don't like, if you want to cleanup

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
Expand All @@ -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();
Comment on lines +174 to +176

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we don't have a sentinel, or we can't convert it, throw an error. A very specific one as well, INVALID_SENTINEL. something like that. Sentinels exist to be unchanged and always correct.

let kernel_version = LittleEndian::read_uint(&kernel_attr_binary[95..96], 1);

let app_memory_len = LittleEndian::read_u32(&kernel_attr_binary[84..92]);
Expand Down
2 changes: 2 additions & 0 deletions tockloader-lib/src/board_settings.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#[derive(Clone)]
pub struct BoardSettings {
pub arch: Option<String>,
pub flash_address: u64,
pub start_address: u64,
Comment on lines +4 to 5

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, but I've changed my mind, let's not have 1-to-1 parity with tockloader-python in the naming scheme here cause this is confusing. We can do

/// Previously named 'flash_address' in the python version of tockloader
pub flash_start_address: u64,
/// Previously named `start_address` in the python version of tockloader
pub app_start_Address: u64,

pub page_size: u64,
pub ram_start_address: u64,
Expand All @@ -12,6 +13,7 @@ impl Default for BoardSettings {
fn default() -> Self {
Self {
arch: None,
flash_address: 0x00000, // this would be actually like -0x10000

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can flash be negative?

start_address: 0x30000,
page_size: 512,
ram_start_address: 0x20000000,
Expand Down
1 change: 0 additions & 1 deletion tockloader-lib/src/bootloader_serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
4 changes: 2 additions & 2 deletions tockloader-lib/src/command_impl/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use crate::{CommandInfo, IOCommands};
#[async_trait]
impl CommandInfo for TockloaderConnection {
async fn info(&mut self) -> Result<GeneralAttributes, TockloaderError> {
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?;
Comment on lines +11 to +12

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Nice!

Ok(GeneralAttributes::new(system_atributes, installed_apps))
}
}
5 changes: 4 additions & 1 deletion tockloader-lib/src/command_impl/probers/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
4 changes: 4 additions & 0 deletions tockloader-lib/src/command_impl/reshuffle_apps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
5 changes: 4 additions & 1 deletion tockloader-lib/src/command_impl/serial/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
6 changes: 5 additions & 1 deletion tockloader-lib/src/known_boards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
}
Expand Down