diff options
| -rw-r--r-- | CHANGELOG.md | 4 | ||||
| -rw-r--r-- | board/Cargo.toml | 5 | ||||
| -rw-r--r-- | board/src/teensy4.rs | 15 | ||||
| -rw-r--r-- | src/host/imxrt-boot-header.x | 7 | ||||
| -rw-r--r-- | src/host/imxrt-link.x | 3 | ||||
| -rw-r--r-- | tests/inspect_elf.rs | 158 |
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 { |
