From 0dc0bb391dc8073f07fe7b91fe79a618e8df4dd1 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Tue, 16 Jun 2020 17:22:11 +0800 Subject: [PATCH] add support for loading bitstream from bootimage. --- default.nix | 2 +- src/Cargo.lock | 10 +- src/runtime/src/load_pl.rs | 176 +++++++++++++++++++++++++++++++++++ src/runtime/src/main.rs | 12 ++- src/runtime/src/sd_reader.rs | 4 +- 5 files changed, 192 insertions(+), 12 deletions(-) create mode 100644 src/runtime/src/load_pl.rs diff --git a/default.nix b/default.nix index 693a469..2f3bd0c 100644 --- a/default.nix +++ b/default.nix @@ -15,7 +15,7 @@ let version = "0.1.0"; src = ./src; - cargoSha256 = "1xag87szccdq5zwjy3ymq5w5wzbp9vwlz4fvflz0f6wpq94mj04w"; + cargoSha256 = "0avqvaa6hryzbcy414ykzfc41hb0yifpkpfcikf9q26ay91af596"; nativeBuildInputs = [ pkgs.gnumake diff --git a/src/Cargo.lock b/src/Cargo.lock index be0416c..b911192 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -184,7 +184,7 @@ dependencies = [ [[package]] name = "libasync" version = "0.0.0" -source = "git+https://git.m-labs.hk/M-Labs/zc706.git#a17a5d2925e7e521f31320ccefd7bf3cb61c45a7" +source = "git+https://git.m-labs.hk/M-Labs/zc706.git#191da7c959baeb30e31196f2c35b0790ea25cbf6" dependencies = [ "embedded-hal", "libcortex_a9", @@ -196,7 +196,7 @@ dependencies = [ [[package]] name = "libboard_zynq" version = "0.0.0" -source = "git+https://git.m-labs.hk/M-Labs/zc706.git#a17a5d2925e7e521f31320ccefd7bf3cb61c45a7" +source = "git+https://git.m-labs.hk/M-Labs/zc706.git#191da7c959baeb30e31196f2c35b0790ea25cbf6" dependencies = [ "bit_field", "embedded-hal", @@ -212,7 +212,7 @@ dependencies = [ [[package]] name = "libcortex_a9" version = "0.0.0" -source = "git+https://git.m-labs.hk/M-Labs/zc706.git#a17a5d2925e7e521f31320ccefd7bf3cb61c45a7" +source = "git+https://git.m-labs.hk/M-Labs/zc706.git#191da7c959baeb30e31196f2c35b0790ea25cbf6" dependencies = [ "bit_field", "libregister", @@ -221,7 +221,7 @@ dependencies = [ [[package]] name = "libregister" version = "0.0.0" -source = "git+https://git.m-labs.hk/M-Labs/zc706.git#a17a5d2925e7e521f31320ccefd7bf3cb61c45a7" +source = "git+https://git.m-labs.hk/M-Labs/zc706.git#191da7c959baeb30e31196f2c35b0790ea25cbf6" dependencies = [ "bit_field", "vcell", @@ -231,7 +231,7 @@ dependencies = [ [[package]] name = "libsupport_zynq" version = "0.0.0" -source = "git+https://git.m-labs.hk/M-Labs/zc706.git#a17a5d2925e7e521f31320ccefd7bf3cb61c45a7" +source = "git+https://git.m-labs.hk/M-Labs/zc706.git#191da7c959baeb30e31196f2c35b0790ea25cbf6" dependencies = [ "compiler_builtins", "libboard_zynq", diff --git a/src/runtime/src/load_pl.rs b/src/runtime/src/load_pl.rs new file mode 100644 index 0000000..cc37378 --- /dev/null +++ b/src/runtime/src/load_pl.rs @@ -0,0 +1,176 @@ +use crate::sd_reader; +use core_io::{Error, Read, Seek, SeekFrom}; +use libboard_zynq::{devc, sdio}; +use log::{info, debug}; + +#[derive(Debug)] +pub enum PlLoadingError { + BootImageNotFound, + InvalidBootImageHeader, + MissingBitstreamPartition, + EncryptedBitstream, + IoError(Error), + DevcError(devc::DevcError), +} + +impl From for PlLoadingError { + fn from(error: Error) -> Self { + PlLoadingError::IoError(error) + } +} + +impl From for PlLoadingError { + fn from(error: devc::DevcError) -> Self { + PlLoadingError::DevcError(error) + } +} + +impl core::fmt::Display for PlLoadingError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + use PlLoadingError::*; + match self { + BootImageNotFound => write!( + f, + "Boot image not found, make sure `boot.bin` exists and your SD card is plugged in." + ), + InvalidBootImageHeader => write!( + f, + "Invalid boot image header. Check if the file is correct." + ), + MissingBitstreamPartition => write!( + f, + "Bitstream partition not found. Check your compile configuration." + ), + EncryptedBitstream => write!(f, "Encrypted bitstream is not supported."), + IoError(e) => write!(f, "Error while reading: {}", e), + DevcError(e) => write!(f, "PCAP interface error: {}", e), + } + } +} + +#[repr(C)] +struct PartitionHeader { + pub encrypted_length: u32, + pub unencrypted_length: u32, + pub word_length: u32, + pub dest_load_addr: u32, + pub dest_exec_addr: u32, + pub data_offset: u32, + pub attribute_bits: u32, + pub section_count: u32, + pub checksum_offset: u32, + pub header_offset: u32, + pub cert_offset: u32, + pub reserved: [u32; 4], + pub checksum: u32, +} + +/// Read a u32 word from the reader. +fn read_u32(reader: &mut Reader) -> Result { + let mut buffer: [u8; 4] = [0; 4]; + reader.read_exact(&mut buffer)?; + let mut result: u32 = 0; + for i in 0..4 { + result |= (buffer[i] as u32) << (i * 8); + } + Ok(result) +} + +/// Load PL partition header. +fn load_pl_header( + file: &mut File, +) -> Result, PlLoadingError> { + let mut buffer: [u8; 0x40] = [0; 0x40]; + file.read_exact(&mut buffer)?; + let header = unsafe { core::mem::transmute::<_, PartitionHeader>(buffer) }; + if header.attribute_bits & (2 << 4) != 0 { + Ok(Some(header)) + } else { + Ok(None) + } +} + +/// Locate the PL bitstream from the image, and return the size (in bytes) of the bitstream if successful. +/// This function would seek the file to the location of the bitstream. +fn locate_bitstream(file: &mut File) -> Result { + const BOOT_HEADER_SIGN: u32 = 0x584C4E58; + // read boot header signature + file.seek(SeekFrom::Start(0x24))?; + if read_u32(file)? != BOOT_HEADER_SIGN { + return Err(PlLoadingError::InvalidBootImageHeader); + } + // read partition header offset + file.seek(SeekFrom::Start(0x9C))?; + let ptr = read_u32(file)?; + debug!("Partition header pointer = {:0X}", ptr); + file.seek(SeekFrom::Start(ptr as u64))?; + + let mut header_opt = None; + // at most 3 partition headers + for _ in 0..3 { + let result = load_pl_header(file)?; + if let Some(h) = result { + header_opt = Some(h); + break; + } + } + let header = match header_opt { + None => return Err(PlLoadingError::MissingBitstreamPartition), + Some(h) => h, + }; + + let encrypted_length = header.encrypted_length; + let unencrypted_length = header.unencrypted_length; + debug!("Unencrypted length = {:0X}", unencrypted_length); + if encrypted_length != unencrypted_length { + return Err(PlLoadingError::EncryptedBitstream); + } + + let start_addr = header.data_offset; + debug!("Partition start address: {:0X}", start_addr); + file.seek(SeekFrom::Start(start_addr as u64 * 4))?; + + Ok(unencrypted_length as usize * 4) +} + +/// Load bitstream from bootgen file. +/// This function parses the file, locate the bitstream and load it through the PCAP driver. +/// It requires a large buffer, please enable the DDR RAM before using it. +pub fn load_bitstream( + file: &mut File, +) -> Result<(), PlLoadingError> { + let size = locate_bitstream(file)?; + let mut buffer: alloc::vec::Vec = alloc::vec::Vec::with_capacity(size); + unsafe { + buffer.set_len(buffer.capacity()); + } + file.read_exact(&mut buffer)?; + + let mut devcfg = devc::DevC::new(); + devcfg.enable(); + devcfg.program(&buffer)?; + Ok(()) +} + +pub fn load_bitstream_from_sd() -> Result<(), PlLoadingError> { + let sdio0 = sdio::SDIO::sdio0(true); + if sdio0.is_card_inserted() { + info!("Card inserted. Mounting file system."); + let mut sd = sdio::sd_card::SdCard::from_sdio(sdio0).unwrap(); + let reader = sd_reader::SdReader::new(&mut sd); + + let fs = reader.mount_fatfs(sd_reader::PartitionEntry::Entry1)?; + let root_dir = fs.root_dir(); + for entry in root_dir.iter() { + if let Ok(entry) = entry { + if entry.is_file() && entry.short_file_name() == "BOOT.BIN" { + info!("Found boot image!"); + return load_bitstream(&mut entry.to_file()); + } + } + } + } else { + info!("SD card not inserted. Bitstream cannot be loaded.") + } + Err(PlLoadingError::BootImageNotFound) +} diff --git a/src/runtime/src/main.rs b/src/runtime/src/main.rs index 3f7f064..5ee4970 100644 --- a/src/runtime/src/main.rs +++ b/src/runtime/src/main.rs @@ -22,7 +22,7 @@ mod pl; mod rtio; mod kernel; mod moninj; - +mod load_pl; fn identifier_read(buf: &mut [u8]) -> &str { unsafe { @@ -51,15 +51,17 @@ pub fn main_core0() { Err(error) => info!("failed to read config: {}", error), } - let devc = devc::DevC::new(); - if devc.is_done() { + if devc::DevC::new().is_done() { info!("gateware already loaded"); // Do not load again: assume that the gateware already present is // what we want (e.g. gateware configured via JTAG before PS // startup, or by FSBL). } else { - info!("loading gateware"); - unimplemented!("gateware loading"); + // Load from SD card + match load_pl::load_bitstream_from_sd() { + Ok(_) => info!("Bitstream loaded successfully!"), + Err(e) => info!("Failure loading bitstream: {}", e), + } } info!("detected gateware: {}", identifier_read(&mut [0; 64])); diff --git a/src/runtime/src/sd_reader.rs b/src/runtime/src/sd_reader.rs index 4400501..a99423b 100644 --- a/src/runtime/src/sd_reader.rs +++ b/src/runtime/src/sd_reader.rs @@ -1,6 +1,7 @@ use core_io::{BufRead, Error, ErrorKind, Read, Result as IoResult, Seek, SeekFrom, Write}; use fatfs; use libboard_zynq::sdio::{sd_card::SdCard, CmdTransferError}; +use log::debug; fn cmd_error_to_io_error(_: CmdTransferError) -> Error { Error::new(ErrorKind::Other, "Command transfer error") @@ -145,8 +146,9 @@ impl<'a> SdReader<'a> { // Read partition ID. self.seek(SeekFrom::Start(entry as u64 + 0x4))?; self.read_exact(&mut buffer[..1])?; + debug!("Partition ID: {:0X}", buffer[0]); match buffer[0] { - 0x01 | 0x04 | 0x06 | 0x0B => (), + 0x01 | 0x04 | 0x06 | 0x0B | 0x0C => (), _ => { return Err(Error::new( ErrorKind::InvalidData,