aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md4
-rw-r--r--board/Cargo.toml5
-rw-r--r--board/src/teensy4.rs15
-rw-r--r--src/host/imxrt-boot-header.x7
-rw-r--r--src/host/imxrt-link.x3
-rw-r--r--tests/inspect_elf.rs158
6 files changed, 176 insertions, 16 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1ee0bb2..f0b8c94 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
## [Unreleased]
+Add section for device configuration data (DCD) in linker script. Users
+can place their DCD in a section called `.dcd`. Consider using imxrt-dcd
+as a convenient way to define a DCD.
+
## [0.1.1] 2023-02-14
Update to cortex-m-rt 0.7.3 to avoid certain miscompilation opportunities.
diff --git a/board/Cargo.toml b/board/Cargo.toml
index 21defd4..9072274 100644
--- a/board/Cargo.toml
+++ b/board/Cargo.toml
@@ -45,3 +45,8 @@ imxrt1170evk-cm7 = [
"dep:rtt-target",
"dep:panic-rtt-target",
]
+
+# Dummy boards for testing DCD linking.
+# Don't try running these on hardware; they might not work.
+__dcd = ["teensy4"]
+__dcd_missize = ["teensy4"]
diff --git a/board/src/teensy4.rs b/board/src/teensy4.rs
index 28587cd..2aaee82 100644
--- a/board/src/teensy4.rs
+++ b/board/src/teensy4.rs
@@ -35,3 +35,18 @@ pub fn prepare(timer_delay_microseconds: u32) -> Option<crate::Resources> {
pit,
})
}
+
+/// Dummy DCD section containing a single NOP command (for testing linker scripts).
+#[cfg(feature = "__dcd")]
+#[link_section = ".dcd"]
+#[no_mangle]
+#[used]
+pub static DEVICE_CONFIGURATION_DATA: [u8; 8] = [0xD2, 0x00, 0x08, 0x41, 0xC0, 0x00, 0x04, 0x00];
+
+/// Ditto but incorrect size (not a multiple of 4 bytes). The linker script should catch this error
+/// and fail the build.
+#[cfg(feature = "__dcd_missize")]
+#[link_section = ".dcd"]
+#[no_mangle]
+#[used]
+pub static DEVICE_CONFIGURATION_DATA: [u8; 7] = [0xD2, 0x00, 0x08, 0x41, 0xC0, 0x00, 0x04];
diff --git a/src/host/imxrt-boot-header.x b/src/host/imxrt-boot-header.x
index 296eff5..ed9d789 100644
--- a/src/host/imxrt-boot-header.x
+++ b/src/host/imxrt-boot-header.x
@@ -53,7 +53,7 @@ SECTIONS
LONG(0x402000D1); /* Header, magic number */
LONG(__sivector_table); /* Address of the vectors table */
LONG(0x00000000); /* RESERVED */
- LONG(0x00000000); /* Device Configuration Data (unused) */
+ LONG(__dcd); /* Device Configuration Data */
LONG(__boot_data); /* Address to boot data */
LONG(__ivt); /* Self reference */
LONG(0x00000000); /* Command Sequence File (unused) */
@@ -67,6 +67,11 @@ SECTIONS
LONG(__image_size); /* Length of image */
LONG(0x00000000); /* Plugin flag (unused) */
LONG(0xDEADBEEF); /* Dummy to align boot data to 16 bytes */
+ . = ALIGN(4);
+ __dcd_start = .;
+ KEEP(*(.dcd)); /* Device Configuration Data */
+ __dcd_end = .;
+ __dcd = ((__dcd_end - __dcd_start) > 0) ? __dcd_start : 0;
*(.Reset); /* Jam the imxrt-rt reset handler into flash. */
*(.__pre_init); /* Also jam the pre-init function, since we need it to run before instructions are placed. */
. = ORIGIN(FLASH) + 0x2000; /* Reserve the remaining 8K as a convenience for a non-XIP boot. */
diff --git a/src/host/imxrt-link.x b/src/host/imxrt-link.x
index d02a170..71b697e 100644
--- a/src/host/imxrt-link.x
+++ b/src/host/imxrt-link.x
@@ -191,6 +191,9 @@ ERROR(imxrt-rt): .got section detected in the input object files
Dynamic relocations are not supported. If you are linking to C code compiled using
the 'cc' crate then modify your build script to compile the C code _without_
the -fPIC flag. See the documentation of the `cc::Build.pic` method for details.");
+
+ASSERT((__dcd_end - __dcd_start) % 4 == 0, "
+ERROR(imxrt-rt): .dcd (Device Configuration Data) size must be a multiple of 4 bytes.");
/* Do not exceed this mark in the error messages above | */
/* ===--- End imxrt-link.x ---=== */
diff --git a/tests/inspect_elf.rs b/tests/inspect_elf.rs
index c45260d..fa2c11d 100644
--- a/tests/inspect_elf.rs
+++ b/tests/inspect_elf.rs
@@ -12,7 +12,7 @@ type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
/// Build an example, returning a path to the ELF.
fn cargo_build(board: &str) -> Result<PathBuf> {
- Command::new("cargo")
+ let status = Command::new("cargo")
.arg("build")
.arg("--example=blink-rtic")
.arg(format!("--features=board/{},board/rtic", board))
@@ -22,6 +22,15 @@ fn cargo_build(board: &str) -> Result<PathBuf> {
.spawn()?
.wait()?;
+ // TODO(summivox): `ExitStatus::exit_ok()` stabilization (can be chained after the `.wait()?)
+ if !status.success() {
+ return Err(format!(
+ "Building board '{}' failed: process returned {:?}",
+ board, status,
+ )
+ .into());
+ }
+
let path = PathBuf::from(format!(
"target/{}/thumbv7em-none-eabihf/debug/examples/blink-rtic",
board
@@ -31,11 +40,23 @@ fn cargo_build(board: &str) -> Result<PathBuf> {
struct ImxrtBinary<'a> {
elf: &'a Elf<'a>,
+ contents: &'a [u8],
+}
+
+/// Image vector table.
+///
+/// Not to be confused with the ARM vector table. See the linker
+/// script for more information.
+struct Ivt {
+ magic_header: u32,
+ interrupt_vector_table: u32,
+ device_configuration_data: u32,
+ boot_data: u32,
}
impl<'a> ImxrtBinary<'a> {
- fn new(elf: &'a Elf<'a>) -> Self {
- Self { elf }
+ fn new(elf: &'a Elf<'a>, contents: &'a [u8]) -> Self {
+ Self { elf, contents }
}
fn symbol(&self, symbol_name: &str) -> Option<goblin::elf::Sym> {
@@ -47,6 +68,10 @@ impl<'a> ImxrtBinary<'a> {
.map(|(sym, _)| sym)
}
+ fn symbol_value(&self, symbol_name: &str) -> Option<u64> {
+ self.symbol(symbol_name).map(|sym| sym.st_value)
+ }
+
fn fcb(&self) -> Result<Fcb> {
self.symbol("FLEXSPI_CONFIGURATION_BLOCK")
.map(|sym| Fcb {
@@ -58,13 +83,35 @@ impl<'a> ImxrtBinary<'a> {
})
}
+ fn read_u32(&self, offset: usize) -> u32 {
+ u32::from_le_bytes(self.contents[offset..offset + 4].try_into().unwrap())
+ }
+
+ fn ivt(&self) -> Result<Ivt> {
+ let ivt_at_runtime = self
+ .symbol_value("__ivt")
+ .ok_or_else(|| String::from("Could not find __ivt symbol"))?;
+ let (boot_section_offset, boot_section_at_runtime) = self
+ .section_header(".boot")
+ .map(|sec| (sec.sh_offset, sec.sh_addr))
+ .ok_or_else(|| String::from("Could not find '.boot' section"))?;
+ let ivt_offset =
+ (boot_section_offset + (ivt_at_runtime - boot_section_at_runtime)) as usize;
+ Ok(Ivt {
+ magic_header: self.read_u32(ivt_offset),
+ interrupt_vector_table: self.read_u32(ivt_offset + 4),
+ device_configuration_data: self.read_u32(ivt_offset + 12),
+ boot_data: self.read_u32(ivt_offset + 16),
+ })
+ }
+
fn flexram_config(&self) -> Result<u64> {
self.symbol("__flexram_config")
.map(|sym| sym.st_value)
.ok_or_else(|| String::from("Could not find FlexRAM configuration in program").into())
}
- fn section(&self, section_name: &str) -> Result<Section> {
+ fn section_header(&self, section_name: &str) -> Option<&goblin::elf::SectionHeader> {
self.elf
.section_headers
.iter()
@@ -75,7 +122,12 @@ impl<'a> ImxrtBinary<'a> {
.map(|name| (sec, name))
})
.find(|(_, name)| section_name == *name)
- .map(|(sec, _)| Section {
+ .map(|(sec, _)| sec)
+ }
+
+ fn section(&self, section_name: &str) -> Result<Section> {
+ self.section_header(section_name)
+ .map(|sec| Section {
address: sec.sh_addr,
size: sec.sh_size,
})
@@ -121,7 +173,11 @@ fn imxrt1010evk() {
let contents = fs::read(path).expect("Could not read ELF file");
let elf = Elf::parse(&contents).expect("Could not parse ELF");
- let binary = ImxrtBinary::new(&elf);
+ let binary = ImxrtBinary::new(&elf, &contents);
+ assert_eq!(
+ binary.symbol_value("__dcd_start"),
+ binary.symbol_value("__dcd_end")
+ );
assert_eq!(
Fcb {
address: 0x6000_0400,
@@ -131,6 +187,15 @@ fn imxrt1010evk() {
);
assert_eq!(binary.flexram_config().unwrap(), 0b11_10_0101);
+ let ivt = binary.ivt().unwrap();
+ assert_eq!(ivt.magic_header, 0x402000D1);
+ assert_eq!(ivt.interrupt_vector_table, 0x6000_2000);
+ assert_eq!(ivt.device_configuration_data, 0);
+ assert_eq!(
+ ivt.boot_data as u64,
+ binary.symbol_value("__ivt").unwrap() + 32
+ );
+
let stack = binary.section(".stack").unwrap();
assert_eq!(
Section {
@@ -218,14 +283,7 @@ fn imxrt1010evk() {
assert_eq!(binary.section_lma(&heap), heap.address, "Heap is NOLOAD");
}
-#[test]
-#[ignore = "building an example can take time"]
-fn teensy4() {
- let path = cargo_build("teensy4").expect("Unable to build example");
- let contents = fs::read(path).expect("Could not read ELF file");
- let elf = Elf::parse(&contents).expect("Could not parse ELF");
-
- let binary = ImxrtBinary::new(&elf);
+fn baseline_teensy4(binary: &ImxrtBinary, dcd_at_runtime: u32) {
assert_eq!(
Fcb {
address: 0x6000_0000,
@@ -238,6 +296,15 @@ fn teensy4() {
0b11111111_101010101010101010101010
);
+ let ivt = binary.ivt().unwrap();
+ assert_eq!(ivt.magic_header, 0x402000D1);
+ assert_eq!(ivt.interrupt_vector_table, 0x6000_2000);
+ assert_eq!(ivt.device_configuration_data, dcd_at_runtime);
+ assert_eq!(
+ ivt.boot_data as u64,
+ binary.symbol_value("__ivt").unwrap() + 32
+ );
+
let stack = binary.section(".stack").unwrap();
assert_eq!(
Section {
@@ -337,12 +404,64 @@ fn teensy4() {
#[test]
#[ignore = "building an example can take time"]
+fn teensy4() {
+ let path = cargo_build("teensy4").expect("Unable to build example");
+ let contents = fs::read(path).expect("Could not read ELF file");
+ let elf = Elf::parse(&contents).expect("Could not parse ELF");
+
+ let binary = ImxrtBinary::new(&elf, &contents);
+ assert!(binary.symbol("DEVICE_CONFIGURATION_DATA").is_none());
+ assert_eq!(
+ binary.symbol_value("__dcd_start"),
+ binary.symbol_value("__dcd_end")
+ );
+ assert_eq!(binary.symbol_value("__dcd"), Some(0));
+ baseline_teensy4(&binary, 0);
+}
+
+#[test]
+#[ignore = "building an example can take time"]
+fn teensy4_fake_dcd() {
+ let path = cargo_build("__dcd").expect("Unable to build example");
+ let contents = fs::read(path).expect("Could not read ELF file");
+ let elf = Elf::parse(&contents).expect("Could not parse ELF");
+
+ let binary = ImxrtBinary::new(&elf, &contents);
+ let dcd = binary.symbol("DEVICE_CONFIGURATION_DATA").unwrap();
+ let dcd_start = binary.symbol_value("__dcd_start").unwrap();
+ assert_eq!(
+ Some(dcd_start + dcd.st_size),
+ binary.symbol_value("__dcd_end"),
+ );
+ assert_eq!(
+ binary.symbol_value("__dcd"),
+ binary.symbol_value("__dcd_start"),
+ );
+ assert_eq!(dcd.st_size % 4, 0);
+ baseline_teensy4(&binary, dcd_start as u32);
+}
+
+#[test]
+#[ignore = "building an example can take time"]
+fn teensy4_fake_dcd_missize_fail() {
+ cargo_build("__dcd_missize").expect_err("Build should fail for missized DCD section.");
+ eprintln!();
+ eprintln!("NOTE: Linker failures above are intentional --- this test has succeeded.");
+}
+
+#[test]
+#[ignore = "building an example can take time"]
fn imxrt1170evk_cm7() {
let path = cargo_build("imxrt1170evk-cm7").expect("Unable to build example");
let contents = fs::read(path).expect("Could not read ELF file");
let elf = Elf::parse(&contents).expect("Could not parse ELF");
- let binary = ImxrtBinary::new(&elf);
+ let binary = ImxrtBinary::new(&elf, &contents);
+ assert_eq!(
+ binary.symbol_value("__dcd_start"),
+ binary.symbol_value("__dcd_end")
+ );
+ assert_eq!(binary.symbol_value("__dcd"), Some(0));
assert_eq!(
Fcb {
address: 0x3000_0400,
@@ -355,6 +474,15 @@ fn imxrt1170evk_cm7() {
0b1111111111111111_1010101010101010
);
+ let ivt = binary.ivt().unwrap();
+ assert_eq!(ivt.magic_header, 0x402000D1);
+ assert_eq!(ivt.interrupt_vector_table, 0x3000_2000);
+ assert_eq!(ivt.device_configuration_data, 0);
+ assert_eq!(
+ ivt.boot_data as u64,
+ binary.symbol_value("__ivt").unwrap() + 32
+ );
+
let stack = binary.section(".stack").unwrap();
assert_eq!(
Section {