consistency changes, workflow fix
Some checks failed
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled

This commit is contained in:
Robin Mueller
2025-10-07 12:52:30 +02:00
parent 6921a6fd95
commit a194dda532
24 changed files with 30 additions and 27 deletions

View File

@@ -0,0 +1,9 @@
[package]
name = "zynq7000-boot-image"
version = "0.1.0"
edition = "2024"
[dependencies]
thiserror = { version = "2", default-features = false }
arbitrary-int = "2"
bitbybit = "1.4"

View File

@@ -0,0 +1,437 @@
#![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;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
pub enum InvalidBootHeader {
#[error("image ID invalid")]
ImageIdInvalid,
#[error("checksum is invalid")]
ChecksumInvalid,
#[error("provided data slice too small")]
DataTooSmall,
}
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]) -> Result<Self, InvalidBootHeader> {
if data.len() < Self::FIXED_SIZED_PART {
return Err(InvalidBootHeader::DataTooSmall);
}
let header = BootHeader { data };
if !header.check_image_id_validity() {
return Err(InvalidBootHeader::ImageIdInvalid);
}
if !header.verify_header_checksum() {
return Err(InvalidBootHeader::ChecksumInvalid);
}
Ok(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
}
/// Offset to the FSBL image in bytes. This information can be used to only extract the boot
/// binary metadata (everything except actual partition data).
#[inline]
pub fn source_offset(&self) -> usize {
self.read_u32_le(0x30) as usize
}
#[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<ImageHeaderTable<'a>> {
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<ImageHeaderIterator<'a>> {
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<Self> {
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<usize> {
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<usize> {
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<Self> {
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<Self::Item> {
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<Self> {
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<PartitionHeaderIterator<'a>> {
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<usize> {
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<usize> {
self.read_u32_le(0x04).checked_mul(4).map(|v| v as usize)
}
pub fn image_name_copied(&self, buf: &mut [u8]) -> Result<usize, BufferTooSmallError> {
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<Self::Item> {
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],
}
#[bitbybit::bitenum(u2, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum PartitionOwner {
Fsbl = 0,
Uboot = 1,
}
#[bitbybit::bitenum(u3, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ChecksumType {
None = 0,
Md5 = 1,
}
#[bitbybit::bitenum(u4, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum DestinationDevice {
None = 0,
Ps = 1,
Pl = 2,
Int = 3,
}
#[bitbybit::bitfield(u32, debug)]
pub struct SectionAttributes {
#[bits(16..=17, rw)]
partition_owner: Option<PartitionOwner>,
#[bit(15, rw)]
rsa_signature_present: bool,
#[bits(12..=14, rw)]
checksum_type: Option<ChecksumType>,
#[bits(4..=7, rw)]
destination_device: Option<DestinationDevice>,
}
impl<'a> PartitionHeader<'a> {
pub const SIZE: usize = 0x40;
// TODO: Checksum check.
#[inline]
pub const fn new(header_data: &'a [u8]) -> Result<Self, BufferTooSmallError> {
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<usize> {
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_attributes(&self) -> SectionAttributes {
SectionAttributes::new_with_raw_value(self.section_attributes_raw())
}
#[inline]
pub fn section_attributes_raw(&self) -> u32 {
self.read_u32_le(0x18)
}
#[inline]
pub fn section_count(&self) -> usize {
self.read_u32_le(0x1C) as usize
}
#[inline]
pub fn data_offset(&self) -> Option<usize> {
self.read_u32_le(0x14).checked_mul(4).map(|v| v as usize)
}
}

View File

@@ -0,0 +1,4 @@
/fsbl.elf
/fpga.bit
/application.elf
/boot.bin

View File

@@ -0,0 +1,28 @@
Boot Image Creation Staging
========
This folder provides the basic files required to create bare metal boot images.
This includes a simple `boot.bif` file which can be used by the AMD
[bootgen](https://docs.amd.com/r/en-US/ug1283-bootgen-user-guide) utility
to create a boot binary consisting of
- A first-stage bootloader (FSBL)
- A FPGA bitstream which can be loaded to the Zynq PL by the FSBL.
- A primary application which runs in DDR memory and is also copied to DDR by the FSBL.
## Example for the Zedboard
An example use-case for the Zedboard would be a `boot.bin` containing the `zedboard-fsbl` Rust
FSBL or the Xilinx FSBL, a bitstream `zedboard-rust.bit` generated from the `zedboard-fpga-design`
project or your own Vivado project and finally any primary application of your choice which
should run in DDR.
You can copy the files into the staging area, using the file names `fsbl.elf`, `fpga.bit` and
`application.elf`, which are also ignored by the VCS.
Then, you can simply run `bootgen` to generate the boot binary:
```sh
bootgen -arch zynq -image boot.bif -o boot.bin -w on
```

View File

@@ -0,0 +1,6 @@
all:
{
[bootloader] fsbl.elf
fpga.bit
application.elf
}

1
zynq7000-boot-image/tester/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

305
zynq7000-boot-image/tester/Cargo.lock generated Normal file
View File

@@ -0,0 +1,305 @@
# 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 = "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 = "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 = "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 = "once_cell_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
[[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 = "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",
"zynq-boot-image",
]
[[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 = "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-boot-image"
version = "0.1.0"
dependencies = [
"arbitrary-int 2.0.0",
"bitbybit",
"thiserror",
]

View File

@@ -0,0 +1,10 @@
[workspace]
[package]
name = "tester"
version = "0.1.0"
edition = "2024"
[dependencies]
zynq-boot-image= { path = ".." }
clap = { version = "4", features = ["derive"] }

View File

@@ -0,0 +1,82 @@
//! Small tester app to verify some of the API offers by the zynq-boot-image crate.
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!("--------------------------------------");
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();
if image_header.partition_count() > 0 {
println!("--------------------------------------");
}
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()
);
println!("section attributes: {:?}", partition.section_attributes());
}
println!("--------------------------------------\n\r");
}
}