diff --git a/Cargo.toml b/Cargo.toml index 305fe5c..189dc5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,9 +9,14 @@ members = [ "examples/embassy", "examples/zedboard", "zynq-mmu", - "zedboard-fsbl", "zedboard-bsp", + "zedboard-fsbl", + "zedboard-bsp", + "zynq-boot-image", +] + +exclude = [ + "zynq-boot-image/tester", ] -exclude = ["experiments"] # cargo build/run --release [profile.release] diff --git a/examples/embassy/Cargo.toml b/examples/embassy/Cargo.toml index 06bfe5d..c17151b 100644 --- a/examples/embassy/Cargo.toml +++ b/examples/embassy/Cargo.toml @@ -19,7 +19,7 @@ zynq7000-embassy = { path = "../../zynq7000-embassy" } dht-sensor = { git = "https://github.com/michaelbeaumont/dht-sensor.git", rev = "10319bdeae9ace3bb0fc79a15da2869c5bf50f52", features = ["async"] } static_cell = "2" critical-section = "1" -heapless = "0.8" +heapless = "0.9" embedded-io = "0.6" embedded-hal = "1" fugit = "0.3" @@ -30,7 +30,7 @@ embassy-executor = { git = "https://github.com/us-irs/embassy.git", branch = "co "executor-thread", ]} # TODO: Remove generic-queue-16 feature as soon as upstream executor is used again. -embassy-time = { version = "0.4", features = ["tick-hz-1_000_000", "generic-queue-16"] } +embassy-time = { version = "0.5", features = ["tick-hz-1_000_000", "generic-queue-16"] } [profile.release] codegen-units = 1 diff --git a/examples/zedboard/Cargo.toml b/examples/zedboard/Cargo.toml index 94d878f..477d2b0 100644 --- a/examples/zedboard/Cargo.toml +++ b/examples/zedboard/Cargo.toml @@ -20,8 +20,8 @@ zedboard-bsp = { path = "../../zedboard-bsp" } num_enum = { version = "0.7", default-features = false } l3gd20 = { git = "https://github.com/us-irs/l3gd20.git", branch = "add-async-if" } embedded-io = "0.6" -bitbybit = "1.3" -arbitrary-int = "1.3" +bitbybit = "1.4" +arbitrary-int = "2" embedded-io-async = "0.6" critical-section = "1" static_cell = "2" @@ -37,10 +37,10 @@ embassy-executor = { git = "https://github.com/us-irs/embassy.git", branch = "co "executor-thread", ]} # TODO: Remove generic-queue-16 feature as soon as upstream executor is used again. -embassy-time = { version = "0.4", features = ["tick-hz-1_000_000", "generic-queue-16"] } +embassy-time = { version = "0.5", features = ["tick-hz-1_000_000", "generic-queue-16"] } embassy-net = { version = "0.7", features = ["dhcpv4", "packet-trace", "medium-ethernet", "icmp", "tcp", "udp"] } embassy-sync = { version = "0.7" } -heapless = "0.8" +heapless = "0.9" axi-uartlite = { git = "https://egit.irs.uni-stuttgart.de/rust/axi-uartlite.git" } axi-uart16550 = { git = "https://egit.irs.uni-stuttgart.de/rust/axi-uart16550.git" } diff --git a/zedboard-bsp/Cargo.toml b/zedboard-bsp/Cargo.toml index 8e0f751..f8fa030 100644 --- a/zedboard-bsp/Cargo.toml +++ b/zedboard-bsp/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] zynq7000-hal = { path = "../zynq7000-hal" } -bitbybit = "1.3" -arbitrary-int = "1.3" +bitbybit = "1.4" +arbitrary-int = "2" num_enum = { version = "0.7", default-features = false } thiserror = { version = "2", default-features = false } diff --git a/zedboard-fsbl/Cargo.toml b/zedboard-fsbl/Cargo.toml index e85d54d..204267a 100644 --- a/zedboard-fsbl/Cargo.toml +++ b/zedboard-fsbl/Cargo.toml @@ -17,4 +17,4 @@ embedded-io = "0.6" embedded-hal = "1" fugit = "0.3" log = "0.4" -arbitrary-int = "1.3" +arbitrary-int = "2" diff --git a/zynq-boot-image/Cargo.toml b/zynq-boot-image/Cargo.toml new file mode 100644 index 0000000..03a0c28 --- /dev/null +++ b/zynq-boot-image/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "zynq-boot-image" +version = "0.1.0" +edition = "2024" + +[dependencies] +thiserror = { version = "2", default-features = false } diff --git a/zynq-boot-image/src/lib.rs b/zynq-boot-image/src/lib.rs new file mode 100644 index 0000000..c908577 --- /dev/null +++ b/zynq-boot-image/src/lib.rs @@ -0,0 +1,384 @@ +#![no_std] + +use core::str::Utf8Error; + +/// ASCII 'XLNX' +pub const IMAGE_ID_U32: u32 = 0x584C4E58; + +/// This is the fixed size of the boot header. +pub const FIXED_BOOT_HEADER_SIZE: usize = 0xA0; + +pub struct BootHeader<'a> { + //base_addr: usize, + // Snapshot of the boot header and following data (at least fixed header size) + data: &'a [u8], +} + +impl<'a> BootHeader<'a> { + pub const FIXED_SIZED_PART: usize = FIXED_BOOT_HEADER_SIZE; + + /// Create a new boot header parser structure without performing any additional checks. + pub const fn new_unchecked(data: &'a [u8]) -> Self { + BootHeader { data } + } + + /// Create a new boot header parser structure while also performing any additonal checks. + /// + /// The passed buffer must have a minimal size of [Self::FIXED_SIZED_PART]. + /// This constructor calls [Self::check_image_id_validity] and [Self::verify_header_checksum] + /// to verify whether the boot header structure is actually valid. + pub fn new(data: &'a [u8]) -> Option { + if data.len() < Self::FIXED_SIZED_PART { + return None; + } + let header = BootHeader { data }; + if !header.check_image_id_validity() || !header.verify_header_checksum() { + return None; + } + Some(header) + } + + #[inline] + fn read_u32_le(&self, offset: usize) -> u32 { + let bytes = &self.data[offset..offset + 4]; + u32::from_le_bytes(bytes.try_into().unwrap()) + } + + #[inline] + pub fn image_id(&self) -> u32 { + self.read_u32_le(0x24) + } + + /// Check whether the image ID has the mandatory [IMAGE_ID_U32] value. + #[inline] + pub fn check_image_id_validity(&self) -> bool { + self.image_id() == IMAGE_ID_U32 + } + + #[inline] + pub fn source_offset(&self) -> usize { + self.read_u32_le(0x30) as usize + } + + // The boot image metadata is all the data up to the first partition of the FSBL. + // + // This information can be used to copy all relevant information for image and partition + // parsing into RAM. This includes the image header table, the image headers and the + // partition headers. + //#[inline] + //pub fn boot_image_metadata_len(&self) -> usize { + //self.source_offset() - self.base_addr + //} + + #[inline] + pub fn header_checksum(&self) -> u32 { + self.read_u32_le(0x48) + } + + #[inline] + pub fn image_header_table_offset(&self) -> usize { + self.read_u32_le(0x98) as usize + } + + pub fn image_header_table(&self) -> Option> { + let offset = self.image_header_table_offset(); + if offset + ImageHeaderTable::SIZE > self.data.len() { + return None; + } + Some( + ImageHeaderTable::new( + &self.data[self.image_header_table_offset() + ..self.image_header_table_offset() + ImageHeaderTable::SIZE], + ) + .unwrap(), + ) + } + + pub fn image_header_iterator(&self) -> Option> { + let first_header_offset = self.image_header_table()?.first_image_header_offset()?; + ImageHeaderIterator::new(self.data, first_header_offset) + } + + #[inline] + pub fn partition_header_table_offset(&self) -> usize { + self.read_u32_le(0x9C) as usize + } + + #[inline] + pub fn verify_header_checksum(&self) -> bool { + let checksum = self.header_checksum(); + let mut sum = 0u32; + let mut ofs = 0x20; + while ofs < 0x48 { + sum = sum.wrapping_add(self.read_u32_le(ofs)); + ofs += 4; + } + !sum == checksum + } +} + +pub struct ImageHeaderTable<'a> { + data: &'a [u8], +} + +impl<'a> ImageHeaderTable<'a> { + pub const SIZE: usize = 0x18; + + pub const fn new(data: &'a [u8]) -> Option { + if data.len() != Self::SIZE { + return None; + } + Some(ImageHeaderTable { data }) + } + + #[inline] + fn read_u32_le(&self, offset: usize) -> u32 { + let bytes = &self.data[offset..offset + 4]; + u32::from_le_bytes(bytes.try_into().unwrap()) + } + + #[inline] + pub fn count_of_headers(&self) -> usize { + self.read_u32_le(0x04) as usize + } + + /// Returns [None] if the number of words times 4 exeeds [u32::MAX]. + #[inline] + pub fn first_image_header_offset(&self) -> Option { + self.read_u32_le(0x0C).checked_mul(4).map(|v| v as usize) + } + + /// Returns [None] if the number of words times 4 exeeds [u32::MAX]. + #[inline] + pub fn first_partition_header_offset(&self) -> Option { + self.read_u32_le(0x08).checked_mul(4).map(|v| v as usize) + } +} + +pub struct ImageHeaderIterator<'a> { + data: &'a [u8], + current_header_offset: usize, +} + +impl<'a> ImageHeaderIterator<'a> { + #[inline] + pub const fn new(data: &'a [u8], first_header_offset: usize) -> Option { + if first_header_offset + ImageHeader::MIN_SIZE > data.len() { + return None; + } + Some(ImageHeaderIterator { + data, + current_header_offset: first_header_offset, + }) + } +} +impl<'a> Iterator for ImageHeaderIterator<'a> { + type Item = ImageHeader<'a>; + + fn next(&mut self) -> Option { + if self.current_header_offset == 0 { + return None; + } + let next_image_header = ImageHeader::new(&self.data[self.current_header_offset..])?; + self.current_header_offset = next_image_header.next_image_header_offset()?; + Some(next_image_header) + } +} + +pub struct ImageHeader<'a> { + header_data: &'a [u8], +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)] +#[error("buffer too small")] +pub struct BufferTooSmallError; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)] +pub enum NameParsingError { + #[error("ut8 error")] + Utf8(#[from] Utf8Error), + #[error("buffer too small")] + BufferTooSmall(#[from] BufferTooSmallError), +} + +impl<'a> ImageHeader<'a> { + pub const MIN_SIZE: usize = 0x1C; + + #[inline] + pub fn new(data: &'a [u8]) -> Option { + if data.len() < Self::MIN_SIZE { + return None; + } + let mut current_offset = 0x14; + let mut prev_word = + u32::from_le_bytes(data[current_offset..current_offset + 4].try_into().unwrap()); + current_offset += 4; + // TODO: Upper bound. + loop { + if current_offset + 4 > data.len() { + return None; + } + let current_word = + u32::from_le_bytes(data[current_offset..current_offset + 4].try_into().unwrap()); + current_offset += 4; + if current_word == 0xffff_ffff || prev_word == 0x0000_0000 { + break; + } + prev_word = current_word; + } + Some(ImageHeader { + header_data: &data[0..current_offset], + }) + } + + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + self.header_data.len() + } + + #[inline] + fn read_u32_le(&self, offset: usize) -> u32 { + let bytes = &self.header_data[offset..offset + 4]; + u32::from_le_bytes(bytes.try_into().unwrap()) + } + + pub fn partition_header_iterator(&self, data: &'a [u8]) -> Option> { + let first_partition_header = self.first_partition_header_offset()?; + Some(PartitionHeaderIterator { + data, + current_partition_header_addr: first_partition_header, + current_partition_index: 0, + num_of_partitions: self.partition_count(), + }) + } + + #[inline] + pub fn next_image_header_offset(&self) -> Option { + self.read_u32_le(0x00).checked_mul(4).map(|v| v as usize) + } + + #[inline] + pub fn partition_count(&self) -> usize { + self.read_u32_le(0x0C) as usize + } + + pub fn first_partition_header_offset(&self) -> Option { + self.read_u32_le(0x04).checked_mul(4).map(|v| v as usize) + } + + pub fn image_name_copied(&self, buf: &mut [u8]) -> Result { + let mut current_offset = 0x10; + let mut current_buf_idx = 0; + let mut null_byte_found = false; + loop { + let next_bytes = &self.header_data[current_offset..current_offset + 4]; + for &byte in next_bytes.iter().rev() { + if byte == 0 { + null_byte_found = true; + break; + } + if current_buf_idx >= buf.len() { + return Err(BufferTooSmallError); + } + buf[current_buf_idx] = byte; + current_buf_idx += 1; + } + if null_byte_found { + break; + } + current_offset += 4; + } + Ok(current_buf_idx) + } + + pub fn image_name<'b>(&self, buf: &'b mut [u8]) -> Result<&'b str, NameParsingError> { + let name_len = self.image_name_copied(buf)?; + core::str::from_utf8(&buf[0..name_len]).map_err(NameParsingError::from) + } +} + +pub struct PartitionHeaderIterator<'a> { + data: &'a [u8], + current_partition_header_addr: usize, + current_partition_index: usize, + num_of_partitions: usize, +} + +impl<'a> Iterator for PartitionHeaderIterator<'a> { + type Item = PartitionHeader<'a>; + + fn next(&mut self) -> Option { + if self.current_partition_index >= self.num_of_partitions { + return None; + } + if self.current_partition_header_addr + PartitionHeader::SIZE > self.data.len() { + return None; + } + let header = PartitionHeader::new( + &self.data[self.current_partition_header_addr + ..self.current_partition_header_addr + PartitionHeader::SIZE], + ) + .ok()?; + self.current_partition_index += 1; + self.current_partition_header_addr += 0x40; + Some(header) + } +} + +pub struct PartitionHeader<'a> { + header_data: &'a [u8], +} + +impl<'a> PartitionHeader<'a> { + pub const SIZE: usize = 0x40; + + // TODO: Checksum check. + #[inline] + pub const fn new(header_data: &'a [u8]) -> Result { + if header_data.len() < Self::SIZE { + return Err(BufferTooSmallError); + } + Ok(PartitionHeader { header_data }) + } + + #[inline] + fn read_u32_le(&self, offset: usize) -> u32 { + let bytes = &self.header_data[offset..offset + 4]; + u32::from_le_bytes(bytes.try_into().unwrap()) + } + + #[inline] + pub fn encrypted_partition_length(&self) -> u32 { + self.read_u32_le(0x00) + } + + #[inline] + pub fn unencrypted_partition_length(&self) -> u32 { + self.read_u32_le(0x04) + } + + #[inline] + pub fn total_partition_length(&self) -> Option { + self.read_u32_le(0x08).checked_mul(4).map(|v| v as usize) + } + + #[inline] + pub fn destination_load_address(&self) -> u32 { + self.read_u32_le(0x0C) + } + + #[inline] + pub fn destination_exec_address(&self) -> u32 { + self.read_u32_le(0x10) + } + + #[inline] + pub fn section_count(&self) -> usize { + self.read_u32_le(0x1C) as usize + } + + #[inline] + pub fn data_offset(&self) -> Option { + self.read_u32_le(0x14).checked_mul(4).map(|v| v as usize) + } +} diff --git a/boot-image-staging/.gitignore b/zynq-boot-image/staging/.gitignore similarity index 100% rename from boot-image-staging/.gitignore rename to zynq-boot-image/staging/.gitignore diff --git a/boot-image-staging/README.md b/zynq-boot-image/staging/README.md similarity index 100% rename from boot-image-staging/README.md rename to zynq-boot-image/staging/README.md diff --git a/boot-image-staging/boot.bif b/zynq-boot-image/staging/boot.bif similarity index 57% rename from boot-image-staging/boot.bif rename to zynq-boot-image/staging/boot.bif index 6d03ebf..7f3f789 100644 --- a/boot-image-staging/boot.bif +++ b/zynq-boot-image/staging/boot.bif @@ -2,5 +2,5 @@ all: { [bootloader] fsbl.elf fpga.bit - [load=0x100000] application.elf + application.elf } diff --git a/zynq-boot-image/tester/.gitignore b/zynq-boot-image/tester/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/zynq-boot-image/tester/.gitignore @@ -0,0 +1 @@ +/target diff --git a/zynq-boot-image/tester/Cargo.lock b/zynq-boot-image/tester/Cargo.lock new file mode 100644 index 0000000..708ef6a --- /dev/null +++ b/zynq-boot-image/tester/Cargo.lock @@ -0,0 +1,709 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstream" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "arbitrary-int" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "825297538d77367557b912770ca3083f778a196054b3ee63b22673c4a3cae0a5" + +[[package]] +name = "arbitrary-int" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c858caffa49edfc4ecc45a4bec37abd3e88041a2903816f10f990b7b41abc281" + +[[package]] +name = "arm-targets" +version = "0.2.0" +source = "git+https://github.com/rust-embedded/cortex-ar.git?rev=79dba7000d2090d13823bfb783d9d64be8b778d2#79dba7000d2090d13823bfb783d9d64be8b778d2" + +[[package]] +name = "bitbybit" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec187a89ab07e209270175faf9e07ceb2755d984954e58a2296e325ddece2762" +dependencies = [ + "arbitrary-int 1.3.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "clap" +version = "4.5.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "cortex-ar" +version = "0.2.0" +source = "git+https://github.com/rust-embedded/cortex-ar.git?rev=79dba7000d2090d13823bfb783d9d64be8b778d2#79dba7000d2090d13823bfb783d9d64be8b778d2" +dependencies = [ + "arbitrary-int 1.3.0", + "arm-targets", + "bitbybit", + "num_enum", + "thiserror", +] + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "delegate" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6178a82cf56c836a3ba61a7935cdb1c49bfaa6fa4327cd5bf554a503087de26b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive-mmio" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cb20d58c6618082f91e4f5bde31422e73dd59afeda16c61c94fd15adcf8812b" +dependencies = [ + "derive-mmio-macro", + "rustversion", +] + +[[package]] +name = "derive-mmio-macro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b92bcaf79d5ee81fac0a867e95df6943cde5e42c82059a9ec93fc0c6cb8ae35a" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "embassy-net-driver" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524eb3c489760508f71360112bca70f6e53173e6fe48fc5f0efd0f5ab217751d" + +[[package]] +name = "embassy-sync" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73974a3edbd0bd286759b3d483540f0ebef705919a5f56f4fc7709066f71689b" +dependencies = [ + "cfg-if", + "critical-section", + "embedded-io-async", + "futures-core", + "futures-sink", + "heapless 0.8.0", +] + +[[package]] +name = "embedded-dma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal", +] + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal", + "nb", +] + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "embedded-io-async" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" +dependencies = [ + "embedded-io", +] + +[[package]] +name = "fugit" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" +dependencies = [ + "gcd", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1edcd5a338e64688fbdcb7531a846cfd3476a54784dcb918a0844682bc7ada5" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "num_enum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +dependencies = [ + "critical-section", + "portable-atomic", +] + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "raw-slicee" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d8fb5aaf355f81e39a4834840f68e809ee98c83f556dcfb8c16fd7004fe4dc" +dependencies = [ + "embedded-dma", +] + +[[package]] +name = "ringbuf" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe47b720588c8702e34b5979cb3271a8b1842c7cb6f57408efa70c779363488c" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "smoltcp" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb" +dependencies = [ + "bitflags", + "byteorder", + "cfg-if", + "heapless 0.8.0", + "managed", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "static_cell" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0530892bb4fa575ee0da4b86f86c667132a94b74bb72160f58ee5a4afec74c23" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tester" +version = "0.1.0" +dependencies = [ + "clap", + "zynq7000-hal", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "zynq-mmu" +version = "0.1.0" +dependencies = [ + "cortex-ar", + "thiserror", +] + +[[package]] +name = "zynq7000" +version = "0.1.0" +dependencies = [ + "arbitrary-int 1.3.0", + "bitbybit", + "derive-mmio", + "once_cell", + "rustversion", + "static_assertions", + "thiserror", +] + +[[package]] +name = "zynq7000-hal" +version = "0.1.0" +dependencies = [ + "arbitrary-int 2.0.0", + "bitbybit", + "cortex-ar", + "critical-section", + "delegate", + "embassy-net-driver", + "embassy-sync", + "embedded-hal", + "embedded-hal-async", + "embedded-hal-nb", + "embedded-io", + "embedded-io-async", + "fugit", + "heapless 0.9.1", + "libm", + "log", + "nb", + "num_enum", + "paste", + "raw-slicee", + "ringbuf", + "smoltcp", + "static_assertions", + "static_cell", + "thiserror", + "vcell", + "zynq-mmu", + "zynq7000", +] diff --git a/zynq-boot-image/tester/Cargo.toml b/zynq-boot-image/tester/Cargo.toml new file mode 100644 index 0000000..1073a57 --- /dev/null +++ b/zynq-boot-image/tester/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "tester" +version = "0.1.0" +edition = "2024" + +[dependencies] +zynq-boot-image= { path = ".." } +clap = { version = "4", features = ["derive"] } diff --git a/zynq-boot-image/tester/src/main.rs b/zynq-boot-image/tester/src/main.rs new file mode 100644 index 0000000..b414d9c --- /dev/null +++ b/zynq-boot-image/tester/src/main.rs @@ -0,0 +1,75 @@ +use std::{io::Read, path::Path}; + +use clap::Parser as _; +use zynq_boot_image::{BootHeader, FIXED_BOOT_HEADER_SIZE}; + +#[derive(clap::Parser, Debug)] +#[command(version, about)] +pub struct Cli { + /// Path to boot.bin file to test. + #[arg(short, long)] + path: String, +} + +fn main() { + let cli = Cli::parse(); + let boot_bin = Path::new(&cli.path); + if !boot_bin.exists() { + eprintln!("File not found: {}", boot_bin.display()); + std::process::exit(1); + } + let mut boot_bin_file = std::fs::File::open(boot_bin).expect("failed to open boot.bin file"); + let mut header_buf = Box::new([0u8; 8192]); + boot_bin_file + .read_exact(&mut header_buf[0..FIXED_BOOT_HEADER_SIZE]) + .expect("failed to read boot header"); + let mut boot_header = BootHeader::new(&header_buf[0..FIXED_BOOT_HEADER_SIZE]) + .expect("failed to parse boot header"); + let source_offset = boot_header.source_offset(); + boot_bin_file + .read_exact(&mut header_buf[FIXED_BOOT_HEADER_SIZE..source_offset - FIXED_BOOT_HEADER_SIZE]) + .expect("failed to read full boot binary metadata"); + // Re-assign with newly read data. + boot_header = BootHeader::new_unchecked(&header_buf[0..source_offset]); + let image_header_table = boot_header + .image_header_table() + .expect("failed extracting image header table"); + let image_headers = image_header_table.count_of_headers(); + println!( + "Image headers: {}, first image header offset {}, first partition header offset {}", + image_headers, + image_header_table + .first_image_header_offset() + .expect("failed reading first image header offset"), + image_header_table + .first_partition_header_offset() + .expect("failed reading first partition header offset") + ); + + let image_header_iter = boot_header + .image_header_iterator() + .expect("failed extracting boot header iterator"); + for (idx, image_header) in image_header_iter.enumerate() { + println!( + "Image header {} with partition count {}", + idx, + image_header.partition_count() + ); + let mut test: [u8; 64] = [0; 64]; + let image_name = image_header + .image_name(&mut test) + .expect("image name error"); + println!("image name: {}", image_name); + let partition_iter = image_header + .partition_header_iterator(header_buf.as_slice()) + .unwrap(); + for partition in partition_iter { + println!( + "partition with size {} and load address {:#08x}, section count {}", + partition.total_partition_length().unwrap(), + partition.destination_load_address(), + partition.section_count() + ); + } + } +} diff --git a/zynq7000-embassy/Cargo.toml b/zynq7000-embassy/Cargo.toml index 6482803..8928c1c 100644 --- a/zynq7000-embassy/Cargo.toml +++ b/zynq7000-embassy/Cargo.toml @@ -18,4 +18,4 @@ zynq7000-hal = { path = "../zynq7000-hal" } # embassy-time-driver = { git = "https://github.com/embassy-rs/embassy.git", branch = "main", version = "0.2" } # embassy-time-queue-utils = { git = "https://github.com/embassy-rs/embassy.git", branch = "main", version = "0.1" } embassy-time-driver = "0.2" -embassy-time-queue-utils = "0.1" +embassy-time-queue-utils = "0.3" diff --git a/zynq7000-hal/Cargo.toml b/zynq7000-hal/Cargo.toml index 7f7835c..254d34d 100644 --- a/zynq7000-hal/Cargo.toml +++ b/zynq7000-hal/Cargo.toml @@ -16,8 +16,8 @@ zynq7000 = { path = "../zynq7000" } zynq-mmu = { path = "../zynq-mmu", version = "0.1.0" } static_assertions = "1.1" -bitbybit = "1.3" -arbitrary-int = "1.3" +bitbybit = "1.4" +arbitrary-int = "2" thiserror = { version = "2", default-features = false } num_enum = { version = "0.7", default-features = false } ringbuf = { version = "0.4.8", default-features = false } @@ -25,7 +25,7 @@ embedded-hal-nb = "1" embedded-io = "0.6" embedded-hal = "1" embedded-hal-async = "1" -heapless = "0.8" +heapless = "0.9" static_cell = "2" delegate = "0.13" paste = "1" diff --git a/zynq7000-hal/src/boot_image.rs b/zynq7000-hal/src/boot_image.rs deleted file mode 100644 index 06a263a..0000000 --- a/zynq7000-hal/src/boot_image.rs +++ /dev/null @@ -1,296 +0,0 @@ -/// ASCII 'XLNX' -pub const IMAGE_ID_U32: u32 = 0x584C4E58; - -/// This is the fixed size of the boot header. -pub const FIXED_BOOT_HEADER_SIZE: usize = 0xA0; - -pub struct BootHeader { - base_addr: u32, -} - -impl BootHeader { - /// # Safety - /// - /// This function will perform volatile reads for image header table fields on the given base - /// address. You must ensure that the address contains a valid image header table. - /// At the very least, [FIXED_BOOT_HEADER_SIZE] bytes of the boot header must be readable at - /// the given address. - pub const unsafe fn new(base_addr: u32) -> Self { - BootHeader { base_addr } - } - - #[inline] - pub fn image_id(&self) -> u32 { - unsafe { core::ptr::read_volatile((self.base_addr + 0x24) as *const _) } - } - - /// Check whether the image ID has the mandatory [IMAGE_ID_U32] value. - #[inline] - pub fn check_image_id_validity(&self) -> bool { - self.image_id() == 0x854C4E58 - } - - #[inline] - pub fn source_offset(&self) -> u32 { - unsafe { core::ptr::read_volatile((self.base_addr + 0x30) as *const _) } - } - - /// The boot image metadata is all the data up to the first partition of the FSBL. - /// - /// This information can be used to copy all relevant information for image and partition - /// parsing into RAM. This includes the image header table, the image headers and the - /// partition headers. - #[inline] - pub fn boot_image_metadata_len(&self) -> u32 { - self.source_offset() - self.base_addr - } - - #[inline] - pub fn header_checksum(&self) -> u32 { - unsafe { core::ptr::read_volatile((self.base_addr + 0x48) as *const _) } - } - - #[inline] - pub fn image_header_table_offset(&self) -> u32 { - unsafe { core::ptr::read_volatile((self.base_addr + 0x98) as *const _) } - } - - /// # Safety - /// - /// This function is only safe to use when the image header table structure - /// was copied to the correct offset of the base address used to initialize - /// this boot header structure. - pub unsafe fn image_header_table(&self) -> ImageHeaderTable { - let offset = self.image_header_table_offset(); - unsafe { ImageHeaderTable::new(self.base_addr + offset) } - } - - #[inline] - pub fn partition_header_table_offset(&self) -> u32 { - unsafe { core::ptr::read_volatile((self.base_addr + 0x9C) as *const _) } - } - - #[inline] - pub fn verify_header_checksum(&self) -> bool { - let checksum = unsafe { core::ptr::read_volatile((self.base_addr + 0x48) as *const _) }; - let end_addr = 0x48; - let mut current_addr = 0x20; - let mut sum = 0u32; - while current_addr < end_addr { - sum = sum.wrapping_add(unsafe { core::ptr::read_volatile(current_addr as *const u32) }); - current_addr += 4; - } - !sum == checksum - } -} - -pub struct ImageHeaderTable { - base_addr: u32, -} - -impl ImageHeaderTable { - /// # Safety - /// - /// This function will perform volatile reads for image header table fields on the given base - /// address. You must ensure that the address contains a valid image header table. - /// - /// This is a low-level function. It is recommended to use [BootHeader::image_header_table] - /// to retrieve the header table after checking boot header validity. - pub const unsafe fn new(base_addr: u32) -> Self { - ImageHeaderTable { base_addr } - } - - pub fn image_header_iterator(&self) -> ImageHeaderIterator { - let first_image_header = self.first_image_header_offset(); - ImageHeaderIterator { - current_image_header: first_image_header, - } - } - - #[inline] - pub fn count_of_headers(&self) -> u32 { - unsafe { core::ptr::read_volatile((self.base_addr + 0x04) as *const _) } - } - - #[inline] - pub fn first_partition_header(&self) -> u32 { - unsafe { core::ptr::read_volatile((self.base_addr + 0x08) as *const _) } - } - - #[inline] - pub fn first_image_header_offset(&self) -> u32 { - let words = unsafe { core::ptr::read_volatile((self.base_addr + 0x0C) as *const u32) }; - words * 4 - } - - /// You can also use [Self::image_header_iterator] to retrieve an iterator over all image - /// headers. - #[inline] - pub fn first_image_header(&self) -> ImageHeader { - unsafe { ImageHeader::new(self.base_addr + self.first_image_header_offset()) } - } -} - -pub struct ImageHeaderIterator { - current_image_header: u32, -} - -impl Iterator for ImageHeaderIterator { - type Item = ImageHeader; - - fn next(&mut self) -> Option { - let image_header = unsafe { ImageHeader::new(self.current_image_header) }; - image_header.next_image_header() - } -} - -pub struct ImageHeader { - base_addr: u32, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)] -#[error("buffer too small")] -pub struct BufTooSmallError; - -impl ImageHeader { - /// # Safety - /// - /// This function will perform volatile reads for image header table fields on the given base - /// address. You must ensure that the address contains a valid image header table. - /// - /// This is a low-level function. It is recommended to use - /// [ImageHeaderTable::image_header_iterator] to retrieve the image headers after checking - /// boot header validity. - #[inline] - pub const unsafe fn new(base_addr: u32) -> Self { - ImageHeader { base_addr } - } - - #[inline] - pub fn next_image_header_offset(&self) -> u32 { - let next_image_header_words = - unsafe { core::ptr::read_volatile(self.base_addr as *const u32) }; - next_image_header_words * 4 - } - - #[inline] - pub fn partition_count(&self) -> u32 { - unsafe { core::ptr::read_volatile((self.base_addr + 0x0C) as *const u32) } - } - - pub fn first_partition_header_offset(&self) -> u32 { - let partition_header_word_offset = - unsafe { core::ptr::read_volatile((self.base_addr + 0x04) as *const u32) }; - partition_header_word_offset * 4 - } - - pub fn partition_header_iterator(&self) -> PartitionHeaderIterator { - let first_partition_header = self.first_partition_header_offset(); - PartitionHeaderIterator { - current_partition_header_addr: first_partition_header, - current_partition_index: 0, - num_of_partitions: self.partition_count(), - } - } - - pub fn image_name_copied(&self, buf: &mut [u8]) -> Result { - let mut current_addr = self.base_addr + 0x10; - let mut current_buf_idx = 0; - let mut null_byte_found = false; - loop { - let next_bytes = unsafe { core::slice::from_raw_parts(current_addr as *const u8, 4) }; - for &byte in next_bytes.iter() { - if byte == 0 { - null_byte_found = true; - break; - } - if current_buf_idx >= buf.len() { - return Err(BufTooSmallError); - } - buf[current_buf_idx] = byte; - current_buf_idx += 1; - } - if null_byte_found { - break; - } - current_addr += 4; - } - Ok(current_buf_idx) - } - - pub fn next_image_header(&self) -> Option { - if self.next_image_header_offset() == 0 { - None - } else { - Some(unsafe { ImageHeader::new(self.base_addr + self.next_image_header_offset()) }) - } - } -} - -pub struct PartitionHeaderIterator { - current_partition_header_addr: u32, - current_partition_index: u32, - num_of_partitions: u32, -} - -impl Iterator for PartitionHeaderIterator { - type Item = PartitionHeader; - - fn next(&mut self) -> Option { - if self.current_partition_index >= self.num_of_partitions { - return None; - } - let header = unsafe { PartitionHeader::new(self.current_partition_index) }; - self.current_partition_index += 1; - self.current_partition_header_addr += 0x40; - Some(header) - } -} - -pub struct PartitionHeader { - base_addr: u32, -} - -impl PartitionHeader { - /// # Safety - /// - /// This function will perform volatile reads for partition header fields on the given base - /// address. You must ensure that the address contains a valid partition header. - /// - /// This is a low-level function. It is recommended to use - /// [ImageHeader::partition_header_iterator] to retrieve the all partition headers for a given - /// image header. - #[inline] - pub const unsafe fn new(base_addr: u32) -> Self { - PartitionHeader { base_addr } - } - - #[inline] - pub fn encrypted_partition_length(&self) -> u32 { - unsafe { core::ptr::read_volatile(self.base_addr as *const u32) } - } - - #[inline] - pub fn unencrypted_partition_length(&self) -> u32 { - unsafe { core::ptr::read_volatile((self.base_addr + 0x04) as *const u32) } - } - - #[inline] - pub fn total_partition_length(&self) -> u32 { - let total_words = - unsafe { core::ptr::read_volatile((self.base_addr + 0x08) as *const u32) }; - total_words * 4 - } - - #[inline] - pub fn destination_load_address(&self) -> u32 { - unsafe { core::ptr::read_volatile((self.base_addr + 0x0C) as *const _) } - } - - #[inline] - pub fn data_offset(&self) -> u32 { - let offset_words = - unsafe { core::ptr::read_volatile((self.base_addr + 0x0C) as *const u32) }; - offset_words * 4 - } -} diff --git a/zynq7000-hal/src/lib.rs b/zynq7000-hal/src/lib.rs index f92a800..c37d13e 100644 --- a/zynq7000-hal/src/lib.rs +++ b/zynq7000-hal/src/lib.rs @@ -18,7 +18,6 @@ use zynq7000::{ slcr::{BootModeRegister, BootPllConfig, LevelShifterRegister}, }; -pub mod boot_image; pub mod cache; pub mod clocks; pub mod ddr; diff --git a/zynq7000-rt/Cargo.toml b/zynq7000-rt/Cargo.toml index 034bd29..c977de6 100644 --- a/zynq7000-rt/Cargo.toml +++ b/zynq7000-rt/Cargo.toml @@ -13,7 +13,7 @@ categories = ["embedded", "no-std", "hardware-support"] [dependencies] cortex-a-rt = { version = "0.1", optional = true, features = ["vfp-dp"] } cortex-ar = { version = "0.2", git = "https://github.com/rust-embedded/cortex-ar.git", rev = "79dba7000d2090d13823bfb783d9d64be8b778d2" } -arbitrary-int = "1.3" +arbitrary-int = "2" zynq-mmu = { path = "../zynq-mmu", version = "0.1.0" } [features] diff --git a/zynq7000/Cargo.toml b/zynq7000/Cargo.toml index 78f1465..4de4921 100644 --- a/zynq7000/Cargo.toml +++ b/zynq7000/Cargo.toml @@ -13,8 +13,8 @@ categories = ["embedded", "no-std", "hardware-support"] [dependencies] static_assertions = "1.1" derive-mmio = { version = "0.6", default-features = false } -bitbybit = "1.3" -arbitrary-int = "1.3" +bitbybit = "1.4" +arbitrary-int = "2" rustversion = "1" thiserror = { version = "2", default-features = false } once_cell = { version = "1", default-features = false, features = ["critical-section"] }