aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsummivox <summivox@gmail.com>2023-08-20 23:13:59 -0700
committerIan McIntyre <ianpmcintyre@gmail.com>2023-09-08 19:18:34 -0400
commitc6d3bcfb11800c58339037be58fa9935f816f1bb (patch)
tree4e8b0d6cdae543e0443f4a8d4cf969f4def7a1f3
parente1fddd5b3b678c43ee59835d7b57bbc063a22d28 (diff)
Add optional DCD section in linker script
Users can define their device configuration data (DCD), and place the data in the .dcd section. If the .dcd section has content, the entry in the IVT points at the user's DCD. This plays well with imxrt-dcd.
-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 {