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
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:
9
zynq7000-boot-image/Cargo.toml
Normal file
9
zynq7000-boot-image/Cargo.toml
Normal 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"
|
||||
437
zynq7000-boot-image/src/lib.rs
Normal file
437
zynq7000-boot-image/src/lib.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
4
zynq7000-boot-image/staging/.gitignore
vendored
Normal file
4
zynq7000-boot-image/staging/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/fsbl.elf
|
||||
/fpga.bit
|
||||
/application.elf
|
||||
/boot.bin
|
||||
28
zynq7000-boot-image/staging/README.md
Normal file
28
zynq7000-boot-image/staging/README.md
Normal 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
|
||||
```
|
||||
6
zynq7000-boot-image/staging/boot.bif
Normal file
6
zynq7000-boot-image/staging/boot.bif
Normal file
@@ -0,0 +1,6 @@
|
||||
all:
|
||||
{
|
||||
[bootloader] fsbl.elf
|
||||
fpga.bit
|
||||
application.elf
|
||||
}
|
||||
1
zynq7000-boot-image/tester/.gitignore
vendored
Normal file
1
zynq7000-boot-image/tester/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
305
zynq7000-boot-image/tester/Cargo.lock
generated
Normal file
305
zynq7000-boot-image/tester/Cargo.lock
generated
Normal 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",
|
||||
]
|
||||
10
zynq7000-boot-image/tester/Cargo.toml
Normal file
10
zynq7000-boot-image/tester/Cargo.toml
Normal 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"] }
|
||||
82
zynq7000-boot-image/tester/src/main.rs
Normal file
82
zynq7000-boot-image/tester/src/main.rs
Normal 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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user