Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TBF: Add "Fixed Addresses" TLV #1845

Merged
merged 8 commits into from
May 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 31 additions & 0 deletions doc/TockBinaryFormat.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
+ [`1` Main](#1-main)
+ [`2` Writeable Flash Region](#2-writeable-flash-region)
+ [`3` Package Name](#3-package-name)
+ [`5` Fixed Addresses](#5-fixed-addresses)
- [Code](#code)

<!-- tocstop -->
Expand Down Expand Up @@ -92,6 +93,7 @@ enum TbfHeaderTypes {
TbfHeaderWriteableFlashRegions = 2,
TbfHeaderPackageName = 3,
TbfHeaderPicOption1 = 4,
TbfHeaderFixedAddresses = 5,
}

// Type-length-value header to identify each struct.
Expand Down Expand Up @@ -129,6 +131,12 @@ struct TbfHeaderWriteableFlashRegions {
base: TbfHeaderTlv,
writeable_flash_regions: [TbfHeaderWriteableFlashRegion],
}

// Fixed and required addresses for process RAM and/or process flash.
struct TbfHeaderV2FixedAddresses {
start_process_ram: u32,
start_process_flash: u32,
}
```

Since all headers are a multiple of four bytes, and all TLV structures must be a
Expand Down Expand Up @@ -262,6 +270,29 @@ an UTF-8 encoded package name.

* `package_name` is an UTF-8 encoded package name

#### `5` Fixed Addresses

`Fixed Addresses` allows processes to specify specific addresses they need for
flash and RAM. Tock supports position-independent apps, but not all apps are
position-independent. This allows the kernel (and other tools) to avoid loading
a non-position-independent binary at an incorrect location.

ppannuto marked this conversation as resolved.
Show resolved Hide resolved
```
0 2 4 6 8
+-------------+-------------+---------------------------+
| Type (5) | Length (8) | ram_address |
+-------------+-------------+-------------+-------------+
| flash_address |
+---------------------------+
```

* `ram_address` the address in memory the process's memory address must start
at. If a fixed address is not required this should be set to `0xFFFFFFFF`.
* `flash_address` the address in flash that the process binary (not the
header) must be located at. This would match the value provided for flash to
the linker. If a fixed address is not required this should be set to
`0xFFFFFFFF`.

## Code

The process code itself has no particular format. It will reside in flash,
Expand Down
81 changes: 73 additions & 8 deletions kernel/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,37 @@ use core::cmp::max;

/// Errors that can occur when trying to load and create processes.
pub enum ProcessLoadError {
/// The TBF header for the app could not be successfully parsed.
/// The TBF header for the process could not be successfully parsed.
TbfHeaderParseFailure(tbfheader::TbfParseError),

/// Not enough flash remaining to parse an app and its header.
/// Not enough flash remaining to parse a process and its header.
NotEnoughFlash,

/// Not enough memory to meet the amount requested by an app.
/// Modify your app to request less memory or flash fewer apps or
/// increase the size of the region your board reserves for app memory.
/// Not enough memory to meet the amount requested by a process. Modify the
/// process to request less memory, flash fewer processes, or increase the
/// size of the region your board reserves for process memory.
NotEnoughMemory,

/// An app was loaded with a length in flash that the MPU does not support.
/// The fix is probably to correct the app size, but this could also be caused
/// by a bad MPU implementation.
/// A process was loaded with a length in flash that the MPU does not
/// support. The fix is probably to correct the process size, but this could
/// also be caused by a bad MPU implementation.
MpuInvalidFlashLength,

/// A process specified a fixed memory address that it needs its memory
/// range to start at, and the kernel did not or could not give the process
/// a memory region starting at that address.
MemoryAddressMismatch {
actual_address: u32,
expected_address: u32,
},

/// A process specified that its binary must start at a particular address,
/// and that is not the address the binary is actually placed at.
IncorrectFlashAddress {
actual_address: u32,
expected_address: u32,
},

/// Process loading error due (likely) to a bug in the kernel. If you get
/// this error please open a bug report.
InternalError,
Expand Down Expand Up @@ -76,6 +91,24 @@ impl fmt::Debug for ProcessLoadError {
write!(f, "App flash length not supported by MPU")
}

ProcessLoadError::MemoryAddressMismatch {
actual_address,
expected_address,
} => write!(
f,
"App memory does not match requested address Actual:{:#x}, Expected:{:#x}",
actual_address, expected_address
),

ProcessLoadError::IncorrectFlashAddress {
actual_address,
expected_address,
} => write!(
f,
"App flash does not match requested address. Actual:{:#x}, Expected:{:#x}",
actual_address, expected_address
),

ProcessLoadError::InternalError => write!(f, "Error in kernel. Likely a bug."),
}
}
Expand Down Expand Up @@ -1583,6 +1616,22 @@ impl<C: 'static + Chip> Process<'a, C> {
// header can't parse, we will error right here.
let tbf_header = tbfheader::parse_tbf_header(header_flash, app_version)?;

// First thing: check that the process is at the correct location in
// flash if the TBF header specified a fixed address. If there is a
// mismatch we catch that early.
if let Some(fixed_flash_start) = tbf_header.get_fixed_address_flash() {
// The flash address in the header is based on the app binary,
// so we need to take into account the header length.
let actual_address = app_flash.as_ptr() as u32 + tbf_header.get_protected_size();
let expected_address = fixed_flash_start;
if actual_address != expected_address {
return Err(ProcessLoadError::IncorrectFlashAddress {
actual_address,
expected_address,
});
}
}

let process_name = tbf_header.get_package_name();

// If this isn't an app (i.e. it is padding) or it is an app but it
Expand Down Expand Up @@ -1703,6 +1752,22 @@ impl<C: 'static + Chip> Process<'a, C> {
// Set up process memory.
let app_memory = slice::from_raw_parts_mut(memory_start as *mut u8, memory_size);

// Check if the memory region is valid for the process. If a process
// included a fixed address for the start of RAM in its TBF header (this
// field is optional, processes that are position independent do not
// need a fixed address) then we check that we used the same address
// when we allocated it RAM.
if let Some(fixed_memory_start) = tbf_header.get_fixed_address_ram() {
let actual_address = app_memory.as_ptr() as u32;
let expected_address = fixed_memory_start;
if actual_address != expected_address {
return Err(ProcessLoadError::MemoryAddressMismatch {
actual_address,
expected_address,
});
}
}

// Set the initial process stack and memory to 3072 bytes.
let initial_stack_pointer = memory_start.add(initial_app_memory_size);
let initial_sbrk_pointer = memory_start.add(initial_app_memory_size);
Expand Down
86 changes: 86 additions & 0 deletions kernel/src/tbfheader.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//! Tock Binary Format Header definitions and parsing code.

// Parsing the headers does not require any unsafe operations.
#![forbid(unsafe_code)]

use core::convert::TryInto;
use core::fmt;
use core::iter::Iterator;
Expand Down Expand Up @@ -93,6 +96,7 @@ crate enum TbfHeaderTypes {
TbfHeaderMain = 1,
TbfHeaderWriteableFlashRegions = 2,
TbfHeaderPackageName = 3,
TbfHeaderFixedAddresses = 5,

/// Some field in the header that we do not understand. Since the TLV format
/// specifies the length of each section, if we get a field we do not
Expand Down Expand Up @@ -128,6 +132,30 @@ crate struct TbfHeaderV2WriteableFlashRegion {
writeable_flash_region_size: u32,
}

/// Optional fixed addresses for flash and RAM for this process.
///
/// If a process is compiled for a specific address this header entry lets the
/// kernel know what those addresses are.
///
/// If this header is omitted the kernel will assume that the process is
/// position-independent and can be loaded at any (reasonably aligned) flash
/// address and can be given any (reasonable aligned) memory segment.
///
/// If this header is included, the kernel will check these values when setting
/// up the process. If a process wants to set one fixed address but not the other, the unused one
/// can be set to 0xFFFFFFFF.
#[derive(Clone, Copy, Debug, Default)]
crate struct TbfHeaderV2FixedAddresses {
/// The absolute address of the start of RAM that the process expects. For
/// example, if the process was linked with a RAM region starting at
/// address `0x00023000`, then this would be set to `0x00023000`.
start_process_ram: u32,
/// The absolute address of the start of the process binary. This does _not_
/// include the TBF header. This is the address the process used for the
/// start of flash with the linker.
start_process_flash: u32,
}

// Conversion functions from slices to the various TBF fields.

impl core::convert::TryFrom<&[u8]> for TbfHeaderV2Base {
Expand Down Expand Up @@ -172,6 +200,7 @@ impl core::convert::TryFrom<u16> for TbfHeaderTypes {
1 => Ok(TbfHeaderTypes::TbfHeaderMain),
2 => Ok(TbfHeaderTypes::TbfHeaderWriteableFlashRegions),
3 => Ok(TbfHeaderTypes::TbfHeaderPackageName),
5 => Ok(TbfHeaderTypes::TbfHeaderFixedAddresses),
_ => Ok(TbfHeaderTypes::Unknown),
}
}
Expand Down Expand Up @@ -240,6 +269,25 @@ impl core::convert::TryFrom<&[u8]> for TbfHeaderV2WriteableFlashRegion {
}
}

impl core::convert::TryFrom<&[u8]> for TbfHeaderV2FixedAddresses {
type Error = TbfParseError;

fn try_from(b: &[u8]) -> Result<TbfHeaderV2FixedAddresses, Self::Error> {
Ok(TbfHeaderV2FixedAddresses {
start_process_ram: u32::from_le_bytes(
b.get(0..4)
.ok_or(TbfParseError::InternalError)?
.try_into()?,
),
start_process_flash: u32::from_le_bytes(
b.get(4..8)
.ok_or(TbfParseError::InternalError)?
.try_into()?,
),
})
}
}

/// Single header that can contain all parts of a v2 header.
///
/// Note, this struct limits the number of writeable regions an app can have to
Expand All @@ -251,6 +299,7 @@ crate struct TbfHeaderV2 {
main: Option<TbfHeaderV2Main>,
package_name: Option<&'static str>,
writeable_regions: Option<[Option<TbfHeaderV2WriteableFlashRegion>; 4]>,
fixed_addresses: Option<TbfHeaderV2FixedAddresses>,
}

/// Type that represents the fields of the Tock Binary Format header.
Expand Down Expand Up @@ -351,6 +400,32 @@ impl TbfHeader {
_ => (0, 0),
}
}

/// Get the address in RAM this process was specifically compiled for. If
/// the process is position independent, return `None`.
crate fn get_fixed_address_ram(&self) -> Option<u32> {
let hd = match self {
TbfHeader::TbfHeaderV2(hd) => hd,
_ => return None,
};
match hd.fixed_addresses.as_ref()?.start_process_ram {
0xFFFFFFFF => None,
start => Some(start),
}
ppannuto marked this conversation as resolved.
Show resolved Hide resolved
}

/// Get the address in flash this process was specifically compiled for. If
/// the process is position independent, return `None`.
crate fn get_fixed_address_flash(&self) -> Option<u32> {
let hd = match self {
TbfHeader::TbfHeaderV2(hd) => hd,
_ => return None,
};
match hd.fixed_addresses.as_ref()?.start_process_flash {
0xFFFFFFFF => None,
start => Some(start),
}
}
}

/// Parse the TBF header length and the entire length of the TBF binary.
Expand Down Expand Up @@ -460,6 +535,7 @@ crate fn parse_tbf_header(header: &'static [u8], version: u16) -> Result<TbfHead
let mut wfr_pointer: [Option<TbfHeaderV2WriteableFlashRegion>; 4] =
Default::default();
let mut app_name_str = "";
let mut fixed_address_pointer: Option<TbfHeaderV2FixedAddresses> = None;

// Iterate the remainder of the header looking for TLV entries.
while remaining.len() > 0 {
Expand Down Expand Up @@ -531,6 +607,15 @@ crate fn parse_tbf_header(header: &'static [u8], version: u16) -> Result<TbfHead
.or(Err(TbfParseError::BadProcessName))?;
}

TbfHeaderTypes::TbfHeaderFixedAddresses => {
let entry_len = 8;
if tlv_header.length as usize == entry_len {
fixed_address_pointer = Some(remaining.try_into()?);
} else {
return Err(TbfParseError::BadTlvEntry(tlv_header.tipe as usize));
}
}

_ => {}
}

Expand All @@ -547,6 +632,7 @@ crate fn parse_tbf_header(header: &'static [u8], version: u16) -> Result<TbfHead
main: main_pointer,
package_name: Some(app_name_str),
writeable_regions: Some(wfr_pointer),
fixed_addresses: fixed_address_pointer,
};

Ok(TbfHeader::TbfHeaderV2(tbf_header))
Expand Down