diff --git a/artiq/firmware/Cargo.lock b/artiq/firmware/Cargo.lock index 80933a416..71ea74d8b 100644 --- a/artiq/firmware/Cargo.lock +++ b/artiq/firmware/Cargo.lock @@ -13,6 +13,12 @@ dependencies = [ name = "alloc_list" version = "0.0.0" +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "bare-metal" version = "0.2.5" @@ -332,6 +338,7 @@ dependencies = [ "proto_artiq", "riscv", "smoltcp", + "tar-no-std", "unwind_backtrace", ] @@ -416,6 +423,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tar-no-std" +version = "0.1.8" +source = "git+https://git.m-labs.hk/M-Labs/tar-no-std?rev=2ab6dc5#2ab6dc58e5249c59c4eb03eaf3a119bcdd678d32" +dependencies = [ + "arrayvec", + "bitflags", + "log", +] + [[package]] name = "unicode-xid" version = "0.0.4" diff --git a/artiq/firmware/runtime/Cargo.toml b/artiq/firmware/runtime/Cargo.toml index 16477707b..af09ca646 100644 --- a/artiq/firmware/runtime/Cargo.toml +++ b/artiq/firmware/runtime/Cargo.toml @@ -40,3 +40,7 @@ git = "https://git.m-labs.hk/M-Labs/libfringe.git" rev = "3ecbe5" default-features = false features = ["alloc"] + +[dependencies.tar-no-std] +git = "https://git.m-labs.hk/M-Labs/tar-no-std" +rev = "2ab6dc5" diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index f0970a73f..a27c337ee 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -25,6 +25,8 @@ extern crate board_artiq; extern crate logger_artiq; extern crate proto_artiq; extern crate riscv; +#[cfg(has_drtio)] +extern crate tar_no_std; use alloc::collections::BTreeMap; use core::cell::RefCell; diff --git a/artiq/firmware/runtime/session.rs b/artiq/firmware/runtime/session.rs index d08e42986..e930b2b1b 100644 --- a/artiq/firmware/runtime/session.rs +++ b/artiq/firmware/runtime/session.rs @@ -2,7 +2,10 @@ use core::{mem, str, cell::{Cell, RefCell}, fmt::Write as FmtWrite}; use alloc::{vec::Vec, string::{String, ToString}}; use byteorder::{ByteOrder, NativeEndian}; use cslice::CSlice; +#[cfg(has_drtio)] +use tar_no_std::TarArchiveRef; +use dyld::elf; use io::{Read, Write, Error as IoError}; #[cfg(has_drtio)] use io::Cursor; @@ -45,6 +48,9 @@ pub enum Error { #[fail(display = "DDMA error: {}", _0)] Ddma(#[cause] remote_dma::Error), #[cfg(has_drtio)] + #[fail(display = "subkernel destination is down")] + DestinationDown, + #[cfg(has_drtio)] #[fail(display = "subkernel error: {}", _0)] Subkernel(#[cause] SubkernelError), #[cfg(has_drtio)] @@ -309,6 +315,63 @@ fn kern_run(session: &mut Session) -> Result<(), Error> { kern_acknowledge() } + +fn process_flash_kernel(io: &Io, _aux_mutex: &Mutex, _subkernel_mutex: &Mutex, + _routing_table: &drtio_routing::RoutingTable, + _up_destinations: &Urc>, + session: &mut Session, kernel: &[u8] +) -> Result<(), Error> { + // handle ELF and TAR files + if kernel[0] == elf::ELFMAG0 && kernel[1] == elf::ELFMAG1 && + kernel[2] == elf::ELFMAG2 && kernel[3] == elf::ELFMAG3 { + // assume ELF file, proceed as before + unsafe { + // make a copy as kernel CPU cannot read SPI directly + kern_load(io, session, Vec::from(kernel).as_ref()) + } + } else { + #[cfg(has_drtio)] + { + let archive = TarArchiveRef::new(kernel); + let entries = archive.entries(); + let mut main_lib: Option<&[u8]> = None; + for entry in entries { + if entry.filename().as_str() == "main.elf" { + main_lib = Some(entry.data()); + } else { + // subkernel filename must be in format: + // " .elf" + let filename = entry.filename(); + let mut iter = filename.as_str().split_whitespace(); + let sid: u32 = iter.next().unwrap() + .parse().unwrap(); + let dest: u8 = iter.next().unwrap() + .strip_suffix(".elf").unwrap() + .parse().unwrap(); + let up = { + let up_destinations = _up_destinations.borrow(); + up_destinations[dest as usize] + }; + if up { + let subkernel_lib = entry.data().to_vec(); + subkernel::add_subkernel(io, _subkernel_mutex, sid, dest, subkernel_lib)?; + subkernel::upload(io, _aux_mutex, _subkernel_mutex, _routing_table, sid)?; + } else { + return Err(Error::DestinationDown); + } + } + } + unsafe { + kern_load(io, session, Vec::from(main_lib.unwrap()).as_ref()) + } + } + #[cfg(not(has_drtio))] + { + unexpected!("multi-kernel libraries are not supported in standalone systems") + } + } +} + fn process_host_message(io: &Io, _aux_mutex: &Mutex, _ddma_mutex: &Mutex, _subkernel_mutex: &Mutex, _routing_table: &drtio_routing::RoutingTable, stream: &mut TcpStream, session: &mut Session) -> Result<(), Error> { @@ -777,11 +840,17 @@ fn flash_kernel_worker(io: &Io, aux_mutex: &Mutex, config::read(config_key, |result| { match result { - Ok(kernel) => unsafe { - // kernel CPU cannot access the SPI flash address space directly, - // so make a copy. - kern_load(io, &mut session, Vec::from(kernel).as_ref()) - }, + Ok(kernel) => { + // process .ELF or .TAR kernels + let res = process_flash_kernel(io, aux_mutex, subkernel_mutex, routing_table, up_destinations, &mut session, kernel); + #[cfg(has_drtio)] + match res { + // wait to establish the DRTIO connection + Err(Error::DestinationDown) => io.sleep(500)?, + _ => () + } + res + } _ => Err(Error::KernelNotFound) } })?; diff --git a/flake.nix b/flake.nix index 0f3fc0a45..47c599b20 100644 --- a/flake.nix +++ b/flake.nix @@ -249,6 +249,7 @@ lockFile = ./artiq/firmware/Cargo.lock; outputHashes = { "fringe-1.2.1" = "sha256-m4rzttWXRlwx53LWYpaKuU5AZe4GSkbjHS6oINt5d3Y="; + "tar-no-std-0.1.8" = "sha256-xm17108v4smXOqxdLvHl9CxTCJslmeogjm4Y87IXFuM="; }; }; nativeBuildInputs = [