diff --git a/src/Cargo.lock b/src/Cargo.lock index 739f2e6..1cd121c 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -559,7 +559,9 @@ name = "satman" version = "0.0.0" dependencies = [ "build_zynq", + "byteorder", "core_io", + "crc", "cslice", "embedded-hal", "io", diff --git a/src/libboard_artiq/src/drtioaux_proto.rs b/src/libboard_artiq/src/drtioaux_proto.rs index 6232260..b5b6309 100644 --- a/src/libboard_artiq/src/drtioaux_proto.rs +++ b/src/libboard_artiq/src/drtioaux_proto.rs @@ -287,6 +287,73 @@ pub enum Packet { SubkernelMessageAck { destination: u8, }, + + CoreMgmtGetLogRequest { + destination: u8, + clear: bool, + }, + CoreMgmtClearLogRequest { + destination: u8, + }, + CoreMgmtSetLogLevelRequest { + destination: u8, + log_level: u8, + }, + CoreMgmtSetUartLogLevelRequest { + destination: u8, + log_level: u8, + }, + CoreMgmtConfigReadRequest { + destination: u8, + length: u16, + key: [u8; MASTER_PAYLOAD_MAX_SIZE], + }, + CoreMgmtConfigReadContinue { + destination: u8, + }, + CoreMgmtConfigWriteRequest { + destination: u8, + last: bool, + length: u16, + data: [u8; MASTER_PAYLOAD_MAX_SIZE], + }, + CoreMgmtConfigRemoveRequest { + destination: u8, + length: u16, + key: [u8; MASTER_PAYLOAD_MAX_SIZE], + }, + CoreMgmtConfigEraseRequest { + destination: u8, + }, + CoreMgmtRebootRequest { + destination: u8, + }, + CoreMgmtAllocatorDebugRequest { + destination: u8, + }, + CoreMgmtFlashRequest { + destination: u8, + last: bool, + length: u16, + data: [u8; MASTER_PAYLOAD_MAX_SIZE], + }, + CoreMgmtDropLinkAck { + destination: u8, + }, + CoreMgmtDropLink, + CoreMgmtGetLogReply { + last: bool, + length: u16, + data: [u8; SAT_PAYLOAD_MAX_SIZE], + }, + CoreMgmtConfigReadReply { + last: bool, + length: u16, + value: [u8; SAT_PAYLOAD_MAX_SIZE], + }, + CoreMgmtReply { + succeeded: bool, + }, } impl Packet { @@ -563,6 +630,111 @@ impl Packet { destination: reader.read_u8()?, }, + 0xd0 => Packet::CoreMgmtGetLogRequest { + destination: reader.read_u8()?, + clear: reader.read_bool()?, + }, + 0xd1 => Packet::CoreMgmtClearLogRequest { + destination: reader.read_u8()?, + }, + 0xd2 => Packet::CoreMgmtSetLogLevelRequest { + destination: reader.read_u8()?, + log_level: reader.read_u8()?, + }, + 0xd3 => Packet::CoreMgmtSetUartLogLevelRequest { + destination: reader.read_u8()?, + log_level: reader.read_u8()?, + }, + 0xd4 => { + let destination = reader.read_u8()?; + let length = reader.read_u16()?; + let mut key: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE]; + reader.read_exact(&mut key[0..length as usize])?; + Packet::CoreMgmtConfigReadRequest { + destination: destination, + length: length, + key: key, + } + } + 0xd5 => Packet::CoreMgmtConfigReadContinue { + destination: reader.read_u8()?, + }, + 0xd6 => { + let destination = reader.read_u8()?; + let last = reader.read_bool()?; + let length = reader.read_u16()?; + let mut data: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE]; + reader.read_exact(&mut data[0..length as usize])?; + Packet::CoreMgmtConfigWriteRequest { + destination: destination, + last: last, + length: length, + data: data, + } + } + 0xd7 => { + let destination = reader.read_u8()?; + let length = reader.read_u16()?; + let mut key: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE]; + reader.read_exact(&mut key[0..length as usize])?; + Packet::CoreMgmtConfigRemoveRequest { + destination: destination, + length: length, + key: key, + } + } + 0xd8 => Packet::CoreMgmtConfigEraseRequest { + destination: reader.read_u8()?, + }, + 0xd9 => Packet::CoreMgmtRebootRequest { + destination: reader.read_u8()?, + }, + 0xda => Packet::CoreMgmtAllocatorDebugRequest { + destination: reader.read_u8()?, + }, + 0xdb => { + let destination = reader.read_u8()?; + let last = reader.read_bool()?; + let length = reader.read_u16()?; + let mut data: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE]; + reader.read_exact(&mut data[0..length as usize])?; + Packet::CoreMgmtFlashRequest { + destination: destination, + last: last, + length: length, + data: data, + } + } + 0xdc => Packet::CoreMgmtDropLinkAck { + destination: reader.read_u8()?, + }, + 0xdd => Packet::CoreMgmtDropLink, + 0xde => { + let last = reader.read_bool()?; + let length = reader.read_u16()?; + let mut data: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE]; + reader.read_exact(&mut data[0..length as usize])?; + Packet::CoreMgmtGetLogReply { + last: last, + length: length, + data: data, + } + } + 0xdf => { + let last = reader.read_bool()?; + let length = reader.read_u16()?; + let mut value: [u8; SAT_PAYLOAD_MAX_SIZE] = [0; SAT_PAYLOAD_MAX_SIZE]; + reader.read_exact(&mut value[0..length as usize])?; + Packet::CoreMgmtConfigReadReply { + last: last, + length: length, + value: value, + } + } + 0xe0 => Packet::CoreMgmtReply { + succeeded: reader.read_bool()?, + }, + ty => return Err(Error::UnknownPacket(ty)), }) } @@ -938,6 +1110,107 @@ impl Packet { writer.write_u8(0xcc)?; writer.write_u8(destination)?; } + + Packet::CoreMgmtGetLogRequest { destination, clear } => { + writer.write_u8(0xd0)?; + writer.write_u8(destination)?; + writer.write_bool(clear)?; + } + Packet::CoreMgmtClearLogRequest { destination } => { + writer.write_u8(0xd1)?; + writer.write_u8(destination)?; + } + Packet::CoreMgmtSetLogLevelRequest { destination, log_level } => { + writer.write_u8(0xd2)?; + writer.write_u8(destination)?; + writer.write_u8(log_level)?; + } + Packet::CoreMgmtSetUartLogLevelRequest { destination, log_level } => { + writer.write_u8(0xd3)?; + writer.write_u8(destination)?; + writer.write_u8(log_level)?; + } + Packet::CoreMgmtConfigReadRequest { + destination, + length, + key, + } => { + writer.write_u8(0xd4)?; + writer.write_u8(destination)?; + writer.write_u16(length)?; + writer.write_all(&key[0..length as usize])?; + } + Packet::CoreMgmtConfigReadContinue { destination } => { + writer.write_u8(0xd5)?; + writer.write_u8(destination)?; + } + Packet::CoreMgmtConfigWriteRequest { + destination, + last, + length, + data, + } => { + writer.write_u8(0xd6)?; + writer.write_u8(destination)?; + writer.write_bool(last)?; + writer.write_u16(length)?; + writer.write_all(&data[0..length as usize])?; + } + Packet::CoreMgmtConfigRemoveRequest { + destination, + length, + key, + } => { + writer.write_u8(0xd7)?; + writer.write_u8(destination)?; + writer.write_u16(length)?; + writer.write_all(&key[0..length as usize])?; + } + Packet::CoreMgmtConfigEraseRequest { destination } => { + writer.write_u8(0xd8)?; + writer.write_u8(destination)?; + } + Packet::CoreMgmtRebootRequest { destination } => { + writer.write_u8(0xd9)?; + writer.write_u8(destination)?; + } + Packet::CoreMgmtAllocatorDebugRequest { destination } => { + writer.write_u8(0xda)?; + writer.write_u8(destination)?; + } + Packet::CoreMgmtFlashRequest { + destination, + last, + length, + data, + } => { + writer.write_u8(0xdb)?; + writer.write_u8(destination)?; + writer.write_bool(last)?; + writer.write_u16(length)?; + writer.write_all(&data[..length as usize])?; + } + Packet::CoreMgmtDropLinkAck { destination } => { + writer.write_u8(0xdc)?; + writer.write_u8(destination)?; + } + Packet::CoreMgmtDropLink => writer.write_u8(0xdd)?, + Packet::CoreMgmtGetLogReply { last, length, data } => { + writer.write_u8(0xde)?; + writer.write_bool(last)?; + writer.write_u16(length)?; + writer.write_all(&data[0..length as usize])?; + } + Packet::CoreMgmtConfigReadReply { last, length, value } => { + writer.write_u8(0xdf)?; + writer.write_bool(last)?; + writer.write_u16(length)?; + writer.write_all(&value[0..length as usize])?; + } + Packet::CoreMgmtReply { succeeded } => { + writer.write_u8(0xe0)?; + writer.write_bool(succeeded)?; + } } Ok(()) } @@ -974,7 +1247,8 @@ impl Packet { | Packet::SubkernelLoadRunReply { .. } | Packet::SubkernelMessageAck { .. } | Packet::DmaPlaybackStatus { .. } - | Packet::SubkernelFinished { .. } => false, + | Packet::SubkernelFinished { .. } + | Packet::CoreMgmtDropLinkAck { .. } => false, _ => true, } } diff --git a/src/runtime/src/comms.rs b/src/runtime/src/comms.rs index 6519296..e63dcb4 100644 --- a/src/runtime/src/comms.rs +++ b/src/runtime/src/comms.rs @@ -784,7 +784,11 @@ pub fn main(timer: GlobalTimer, cfg: Config) { let cfg = Rc::new(cfg); let restart_idle = Rc::new(Semaphore::new(1, 1)); - mgmt::start(cfg.clone(), restart_idle.clone()); + mgmt::start( + cfg.clone(), + restart_idle.clone(), + Some(mgmt::DrtioTuple(aux_mutex.clone(), drtio_routing_table.clone(), timer)), + ); task::spawn(async move { let connection = Rc::new(Semaphore::new(1, 1)); @@ -910,7 +914,7 @@ pub fn soft_panic_main(timer: GlobalTimer, cfg: Config) -> ! { Sockets::init(32); let dummy = Rc::new(Semaphore::new(0, 1)); - mgmt::start(Rc::new(cfg), dummy); + mgmt::start(Rc::new(cfg), dummy, None); // getting eth settings disables the LED as it resets GPIO // need to re-enable it here diff --git a/src/runtime/src/mgmt.rs b/src/runtime/src/mgmt.rs index 1a5590f..d87ec50 100644 --- a/src/runtime/src/mgmt.rs +++ b/src/runtime/src/mgmt.rs @@ -3,15 +3,18 @@ use core::cell::RefCell; use futures::{future::poll_fn, task::Poll}; use libasync::{smoltcp::TcpStream, task}; -use libboard_artiq::logger::{BufferLogger, LogBufferRef}; -use libboard_zynq::{slcr, smoltcp}; +use libboard_artiq::{drtio_routing::RoutingTable, + logger::{BufferLogger, LogBufferRef}}; +use libboard_zynq::{smoltcp, timer::GlobalTimer}; use libconfig::Config; -use libcortex_a9::semaphore::Semaphore; -use log::{self, debug, error, info, warn, LevelFilter}; +use libcortex_a9::{mutex::Mutex, semaphore::Semaphore}; +use log::{self, debug, error, info, warn}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; use crate::proto_async::*; +#[cfg(has_drtio)] +use crate::rtio_mgt::drtio; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Error { @@ -19,6 +22,8 @@ pub enum Error { UnknownLogLevel(u8), UnexpectedPattern, UnrecognizedPacket, + #[cfg(has_drtio)] + DrtioError(drtio::Error), } type Result = core::result::Result; @@ -30,6 +35,8 @@ impl core::fmt::Display for Error { &Error::UnknownLogLevel(lvl) => write!(f, "unknown log level {}", lvl), &Error::UnexpectedPattern => write!(f, "unexpected pattern"), &Error::UnrecognizedPacket => write!(f, "unrecognized packet"), + #[cfg(has_drtio)] + &Error::DrtioError(error) => write!(f, "drtio error: {}", error), } } } @@ -40,6 +47,13 @@ impl From for Error { } } +#[cfg(has_drtio)] +impl From for Error { + fn from(error: drtio::Error) -> Self { + Error::DrtioError(error) + } +} + #[derive(Debug, FromPrimitive)] pub enum Request { GetLog = 1, @@ -52,6 +66,11 @@ pub enum Request { ConfigRead = 12, ConfigWrite = 13, ConfigRemove = 14, + ConfigErase = 15, + + DebugAllocator = 8, + + Flash = 9, } #[repr(i8)] @@ -112,15 +131,751 @@ async fn read_key(stream: &mut TcpStream) -> Result { Ok(String::from_utf8(buffer).unwrap()) } +#[cfg(has_drtio)] +mod remote_coremgmt { + use core_io::Read; + use io::ProtoWrite; + use libboard_artiq::{drtioaux_async, + drtioaux_proto::{Packet, MASTER_PAYLOAD_MAX_SIZE}}; + + use super::*; + + pub async fn get_log( + stream: &mut TcpStream, + aux_mutex: &Rc>, + routing_table: &RoutingTable, + timer: GlobalTimer, + linkno: u8, + destination: u8, + ) -> Result<()> { + let mut buffer = Vec::new(); + loop { + let reply = drtio::aux_transact( + aux_mutex, + linkno, + routing_table, + &Packet::CoreMgmtGetLogRequest { + destination, + clear: false, + }, + timer, + ) + .await; + + match reply { + Ok(Packet::CoreMgmtGetLogReply { last, length, data }) => { + buffer.extend(&data[..length as usize]); + if last { + write_i8(stream, Reply::LogContent as i8).await?; + write_chunk(stream, &buffer).await?; + return Ok(()); + } + } + Ok(packet) => { + error!("received unexpected aux packet: {:?}", packet); + write_i8(stream, Reply::Error as i8).await?; + return Err(drtio::Error::UnexpectedReply.into()); + } + Err(e) => { + error!("aux packet error ({})", e); + write_i8(stream, Reply::Error as i8).await?; + return Err(e.into()); + } + } + } + } + + pub async fn clear_log( + stream: &mut TcpStream, + aux_mutex: &Rc>, + routing_table: &RoutingTable, + timer: GlobalTimer, + linkno: u8, + destination: u8, + ) -> Result<()> { + let reply = drtio::aux_transact( + aux_mutex, + linkno, + routing_table, + &Packet::CoreMgmtClearLogRequest { destination }, + timer, + ) + .await; + + match reply { + Ok(Packet::CoreMgmtReply { succeeded: true }) => { + write_i8(stream, Reply::Success as i8).await?; + Ok(()) + } + Ok(packet) => { + error!("received unexpected aux packet: {:?}", packet); + write_i8(stream, Reply::Error as i8).await?; + Err(drtio::Error::UnexpectedReply.into()) + } + Err(e) => { + error!("aux packet error ({})", e); + write_i8(stream, Reply::Error as i8).await?; + Err(e.into()) + } + } + } + + pub async fn pull_log( + stream: &mut TcpStream, + aux_mutex: &Rc>, + routing_table: &RoutingTable, + timer: GlobalTimer, + linkno: u8, + destination: u8, + pull_id: &Rc>, + ) -> Result<()> { + let id = { + let mut guard = pull_id.borrow_mut(); + *guard += 1; + *guard + }; + let mut buffer = Vec::new(); + + loop { + if id != *pull_id.borrow() { + // another connection attempts to pull the log... + // abort this connection... + break; + } + + let reply = drtio::aux_transact( + aux_mutex, + linkno, + routing_table, + &Packet::CoreMgmtGetLogRequest { + destination, + clear: true, + }, + timer, + ) + .await; + + match reply { + Ok(Packet::CoreMgmtGetLogReply { last, length, data }) => { + buffer.extend(&data[..length as usize]); + if last { + write_chunk(stream, &buffer).await?; + buffer.clear(); + task::r#yield().await; + } + } + Ok(packet) => { + error!("received unexpected aux packet: {:?}", packet); + return Err(drtio::Error::UnexpectedReply.into()); + } + Err(e) => { + error!("aux packet error ({})", e); + return Err(e.into()); + } + } + } + + Ok(()) + } + + pub async fn set_log_filter( + stream: &mut TcpStream, + aux_mutex: &Rc>, + routing_table: &RoutingTable, + timer: GlobalTimer, + linkno: u8, + destination: u8, + level: log::LevelFilter, + ) -> Result<()> { + let reply = drtio::aux_transact( + aux_mutex, + linkno, + routing_table, + &Packet::CoreMgmtSetLogLevelRequest { + destination, + log_level: level as u8, + }, + timer, + ) + .await; + + match reply { + Ok(Packet::CoreMgmtReply { succeeded: true }) => { + write_i8(stream, Reply::Success as i8).await?; + Ok(()) + } + Ok(packet) => { + error!("received unexpected aux packet: {:?}", packet); + write_i8(stream, Reply::Error as i8).await?; + Err(drtio::Error::UnexpectedReply.into()) + } + Err(e) => { + error!("aux packet error ({})", e); + write_i8(stream, Reply::Error as i8).await?; + Err(e.into()) + } + } + } + + pub async fn set_uart_log_filter( + stream: &mut TcpStream, + aux_mutex: &Rc>, + routing_table: &RoutingTable, + timer: GlobalTimer, + linkno: u8, + destination: u8, + level: log::LevelFilter, + ) -> Result<()> { + let reply = drtio::aux_transact( + aux_mutex, + linkno, + routing_table, + &Packet::CoreMgmtSetUartLogLevelRequest { + destination, + log_level: level as u8, + }, + timer, + ) + .await; + + match reply { + Ok(Packet::CoreMgmtReply { succeeded: true }) => { + write_i8(stream, Reply::Success as i8).await?; + Ok(()) + } + Ok(packet) => { + error!("received unexpected aux packet: {:?}", packet); + write_i8(stream, Reply::Error as i8).await?; + Err(drtio::Error::UnexpectedReply.into()) + } + Err(e) => { + error!("aux packet error ({})", e); + write_i8(stream, Reply::Error as i8).await?; + Err(e.into()) + } + } + } + + pub async fn config_read( + stream: &mut TcpStream, + aux_mutex: &Rc>, + routing_table: &RoutingTable, + timer: GlobalTimer, + linkno: u8, + destination: u8, + _cfg: &Rc, + key: &String, + ) -> Result<()> { + let mut config_key: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE]; + let len = key.len(); + config_key[..len].clone_from_slice(key.as_bytes()); + + let mut reply = drtio::aux_transact( + aux_mutex, + linkno, + routing_table, + &Packet::CoreMgmtConfigReadRequest { + destination: destination, + length: len as u16, + key: config_key, + }, + timer, + ) + .await; + + let mut buffer = Vec::::new(); + loop { + match reply { + Ok(Packet::CoreMgmtConfigReadReply { last, length, value }) => { + buffer.extend(&value[..length as usize]); + + if last { + write_i8(stream, Reply::ConfigData as i8).await?; + write_chunk(stream, &buffer).await?; + return Ok(()); + } + + reply = drtio::aux_transact( + aux_mutex, + linkno, + routing_table, + &Packet::CoreMgmtConfigReadContinue { + destination: destination, + }, + timer, + ) + .await; + } + Ok(packet) => { + error!("received unexpected aux packet: {:?}", packet); + write_i8(stream, Reply::Error as i8).await?; + return Err(drtio::Error::UnexpectedReply.into()); + } + Err(e) => { + error!("aux packet error ({})", e); + write_i8(stream, Reply::Error as i8).await?; + return Err(e.into()); + } + } + } + } + + pub async fn config_write( + stream: &mut TcpStream, + aux_mutex: &Rc>, + routing_table: &RoutingTable, + timer: GlobalTimer, + linkno: u8, + destination: u8, + _cfg: &Rc, + key: &String, + value: Vec, + _restart_idle: &Rc, + ) -> Result<()> { + let mut message = Vec::with_capacity(key.len() + value.len() + 4 * 2); + message.write_string(key).unwrap(); + message.write_bytes(&value).unwrap(); + + match drtio::partition_data( + linkno, + aux_mutex, + routing_table, + timer, + &message, + |slice, status, len: usize| Packet::CoreMgmtConfigWriteRequest { + destination: destination, + last: status.is_last(), + length: len as u16, + data: *slice, + }, + |reply| match reply { + Packet::CoreMgmtReply { succeeded: true } => Ok(()), + packet => { + error!("received unexpected aux packet: {:?}", packet); + Err(drtio::Error::UnexpectedReply) + } + }, + ) + .await + { + Ok(()) => { + write_i8(stream, Reply::Success as i8).await?; + Ok(()) + } + Err(e) => { + error!("aux packet error ({})", e); + write_i8(stream, Reply::Error as i8).await?; + Err(e.into()) + } + } + } + + pub async fn config_remove( + stream: &mut TcpStream, + aux_mutex: &Rc>, + routing_table: &RoutingTable, + timer: GlobalTimer, + linkno: u8, + destination: u8, + _cfg: &Rc, + key: &String, + _restart_idle: &Rc, + ) -> Result<()> { + let mut config_key: [u8; MASTER_PAYLOAD_MAX_SIZE] = [0; MASTER_PAYLOAD_MAX_SIZE]; + let len = key.len(); + config_key[..len].clone_from_slice(key.as_bytes()); + + let reply = drtio::aux_transact( + aux_mutex, + linkno, + routing_table, + &Packet::CoreMgmtConfigRemoveRequest { + destination: destination, + length: len as u16, + key: config_key, + }, + timer, + ) + .await; + + match reply { + Ok(Packet::CoreMgmtReply { succeeded: true }) => { + write_i8(stream, Reply::Success as i8).await?; + Ok(()) + } + Ok(packet) => { + error!("received unexpected aux packet: {:?}", packet); + write_i8(stream, Reply::Error as i8).await?; + Err(drtio::Error::UnexpectedReply.into()) + } + Err(e) => { + error!("aux packet error ({})", e); + write_i8(stream, Reply::Error as i8).await?; + Err(e.into()) + } + } + } + + pub async fn config_erase( + stream: &mut TcpStream, + aux_mutex: &Rc>, + routing_table: &RoutingTable, + timer: GlobalTimer, + linkno: u8, + destination: u8, + ) -> Result<()> { + let reply = drtio::aux_transact( + aux_mutex, + linkno, + routing_table, + &Packet::CoreMgmtConfigEraseRequest { + destination: destination, + }, + timer, + ) + .await; + + match reply { + Ok(Packet::CoreMgmtReply { succeeded: true }) => { + write_i8(stream, Reply::Success as i8).await?; + Ok(()) + } + Ok(packet) => { + error!("received unexpected aux packet: {:?}", packet); + write_i8(stream, Reply::Error as i8).await?; + Err(drtio::Error::UnexpectedReply.into()) + } + Err(e) => { + error!("aux packet error ({})", e); + write_i8(stream, Reply::Error as i8).await?; + Err(e.into()) + } + } + } + + pub async fn reboot( + stream: &mut TcpStream, + aux_mutex: &Rc>, + routing_table: &RoutingTable, + timer: GlobalTimer, + linkno: u8, + destination: u8, + ) -> Result<()> { + info!("initited reboot request to satellite destination {}", destination); + let reply = drtio::aux_transact( + aux_mutex, + linkno, + routing_table, + &Packet::CoreMgmtRebootRequest { + destination: destination, + }, + timer, + ) + .await; + + match reply { + Ok(Packet::CoreMgmtReply { succeeded: true }) => { + write_i8(stream, Reply::RebootImminent as i8).await?; + Ok(()) + } + Ok(packet) => { + error!("received unexpected aux packet: {:?}", packet); + write_i8(stream, Reply::Error as i8).await?; + Err(drtio::Error::UnexpectedReply.into()) + } + Err(e) => { + error!("aux packet error ({})", e); + write_i8(stream, Reply::Error as i8).await?; + Err(e.into()) + } + } + } + + pub async fn debug_allocator( + stream: &mut TcpStream, + aux_mutex: &Rc>, + routing_table: &RoutingTable, + timer: GlobalTimer, + linkno: u8, + destination: u8, + ) -> Result<()> { + let reply = drtio::aux_transact( + aux_mutex, + linkno, + routing_table, + &Packet::CoreMgmtAllocatorDebugRequest { + destination: destination, + }, + timer, + ) + .await; + + match reply { + Ok(Packet::CoreMgmtReply { succeeded: true }) => { + write_i8(stream, Reply::Success as i8).await?; + Ok(()) + } + Ok(packet) => { + error!("received unexpected aux packet: {:?}", packet); + Err(drtio::Error::UnexpectedReply.into()) + } + Err(e) => { + error!("aux packet error ({})", e); + Err(e.into()) + } + } + } + + pub async fn image_write( + stream: &mut TcpStream, + aux_mutex: &Rc>, + routing_table: &RoutingTable, + timer: GlobalTimer, + linkno: u8, + destination: u8, + _cfg: &Rc, + image: Vec, + ) -> Result<()> { + let mut image = &image[..]; + + while !image.is_empty() { + let mut data = [0; MASTER_PAYLOAD_MAX_SIZE]; + let len = image.read(&mut data).unwrap(); + let last = image.is_empty(); + + let reply = drtio::aux_transact( + aux_mutex, + linkno, + routing_table, + &Packet::CoreMgmtFlashRequest { + destination: destination, + last: last, + length: len as u16, + data: data, + }, + timer, + ) + .await; + + match reply { + Ok(Packet::CoreMgmtReply { succeeded: true }) if !last => Ok(()), + Ok(Packet::CoreMgmtDropLink) if last => drtioaux_async::send( + linkno, + &Packet::CoreMgmtDropLinkAck { + destination: destination, + }, + ) + .await + .map_err(|_| drtio::Error::AuxError), + Ok(packet) => { + error!("received unexpected aux packet: {:?}", packet); + write_i8(stream, Reply::Error as i8).await?; + Err(drtio::Error::UnexpectedReply) + } + Err(e) => { + error!("aux packet error ({})", e); + write_i8(stream, Reply::Error as i8).await?; + Err(drtio::Error::AuxError) + } + }?; + } + + write_i8(stream, Reply::RebootImminent as i8).await?; + Ok(()) + } +} + +mod local_coremgmt { + use libboard_zynq::slcr; + + use super::*; + + pub async fn get_log(stream: &mut TcpStream) -> Result<()> { + let buffer = get_logger_buffer().await.extract().as_bytes().to_vec(); + write_i8(stream, Reply::LogContent as i8).await?; + write_chunk(stream, &buffer).await?; + Ok(()) + } + + pub async fn clear_log(stream: &mut TcpStream) -> Result<()> { + let mut buffer = get_logger_buffer().await; + buffer.clear(); + write_i8(stream, Reply::Success as i8).await?; + Ok(()) + } + + pub async fn pull_log(stream: &mut TcpStream, pull_id: &Rc>) -> Result<()> { + let id = { + let mut guard = pull_id.borrow_mut(); + *guard += 1; + *guard + }; + loop { + let mut buffer = get_logger_buffer_pred(|b| !b.is_empty()).await; + if id != *pull_id.borrow() { + // another connection attempts to pull the log... + // abort this connection... + break; + } + let bytes = buffer.extract().as_bytes().to_vec(); + buffer.clear(); + core::mem::drop(buffer); + write_chunk(stream, &bytes).await?; + if log::max_level() == log::LevelFilter::Trace { + // temporarily discard all trace level log + let logger = unsafe { BufferLogger::get_logger().as_mut().unwrap() }; + logger.set_buffer_log_level(log::LevelFilter::Debug); + stream.flush().await?; + logger.set_buffer_log_level(log::LevelFilter::Trace); + } + } + Ok(()) + } + + pub async fn set_log_filter(stream: &mut TcpStream, lvl: log::LevelFilter) -> Result<()> { + info!("Changing log level to {}", lvl); + log::set_max_level(lvl); + write_i8(stream, Reply::Success as i8).await?; + Ok(()) + } + + pub async fn set_uart_log_filter(stream: &mut TcpStream, lvl: log::LevelFilter) -> Result<()> { + info!("Changing UART log level to {}", lvl); + unsafe { + BufferLogger::get_logger().as_ref().unwrap().set_uart_log_level(lvl); + } + write_i8(stream, Reply::Success as i8).await?; + Ok(()) + } + + pub async fn config_read(stream: &mut TcpStream, cfg: &Rc, key: &String) -> Result<()> { + let value = cfg.read(&key); + if let Ok(value) = value { + debug!("got value"); + write_i8(stream, Reply::ConfigData as i8).await?; + write_chunk(stream, &value).await?; + } else { + warn!("read error: no such key"); + write_i8(stream, Reply::Error as i8).await?; + } + Ok(()) + } + + pub async fn config_write( + stream: &mut TcpStream, + cfg: &Rc, + key: &String, + value: Vec, + restart_idle: &Rc, + ) -> Result<()> { + let value = cfg.write(&key, value); + if value.is_ok() { + debug!("write success"); + if key == "idle_kernel" { + restart_idle.signal(); + } + write_i8(stream, Reply::Success as i8).await?; + } else { + // this is an error because we do not expect write to fail + error!("failed to write: {:?}", value); + write_i8(stream, Reply::Error as i8).await?; + } + Ok(()) + } + + pub async fn config_remove( + stream: &mut TcpStream, + cfg: &Rc, + key: &String, + restart_idle: &Rc, + ) -> Result<()> { + debug!("erase key: {}", key); + let value = cfg.remove(&key); + if value.is_ok() { + debug!("erase success"); + if key == "idle_kernel" { + restart_idle.signal(); + } + write_i8(stream, Reply::Success as i8).await?; + } else { + warn!("erase failed"); + write_i8(stream, Reply::Error as i8).await?; + } + Ok(()) + } + + pub async fn config_erase(stream: &mut TcpStream) -> Result<()> { + error!("zynq device does not support config erase"); + write_i8(stream, Reply::Error as i8).await?; + Ok(()) + } + + pub async fn reboot(stream: &mut TcpStream) -> Result<()> { + info!("rebooting"); + write_i8(stream, Reply::RebootImminent as i8).await?; + stream.flush().await?; + slcr::reboot(); + + unreachable!() + } + + pub async fn debug_allocator(_stream: &mut TcpStream) -> Result<()> { + error!("zynq device does not support allocator debug print"); + Ok(()) + } + + pub async fn image_write(stream: &mut TcpStream, cfg: &Rc, image: Vec) -> Result<()> { + let value = cfg.write("boot", image); + if value.is_ok() { + reboot(stream).await?; + } else { + // this is an error because we do not expect write to fail + error!("failed to write boot file: {:?}", value); + write_i8(stream, Reply::Error as i8).await?; + } + Ok(()) + } +} + +#[cfg(has_drtio)] +macro_rules! process { + ($stream: ident, $drtio_tuple:ident, $destination:expr, $func:ident $(, $param:expr)*) => {{ + if $destination == 0 { + local_coremgmt::$func($stream, $($param, )*).await + } else if let Some(DrtioTuple(ref aux_mutex, ref routing_table, timer)) = $drtio_tuple { + let routing_table = routing_table.borrow(); + let linkno = routing_table.0[$destination as usize][0] - 1 as u8; + remote_coremgmt::$func($stream, &aux_mutex, &routing_table, timer, linkno, $destination, $($param, )*).await + } else { + error!("coremgmt-over-drtio not supported for panicked device, please reboot"); + write_i8($stream, Reply::Error as i8).await?; + Err(drtio::Error::LinkDown.into()) + } + }} +} + +#[cfg(not(has_drtio))] +macro_rules! process { + ($stream: ident, $drtio_tuple:ident, $destination:expr, $func:ident $(, $param:expr)*) => {{ + local_coremgmt::$func($stream, $($param, )*).await + }} +} + +#[derive(Clone)] +pub struct DrtioTuple(pub Rc>, pub Rc>, pub GlobalTimer); + async fn handle_connection( stream: &mut TcpStream, pull_id: Rc>, cfg: Rc, restart_idle: Rc, + _drtio_tuple: Option, ) -> Result<()> { if !expect(&stream, b"ARTIQ management\n").await? { return Err(Error::UnexpectedPattern); } + + let _destination: u8 = read_i8(stream).await? as u8; stream.send_slice("e".as_bytes()).await?; loop { @@ -130,72 +885,23 @@ async fn handle_connection( } let msg: Request = FromPrimitive::from_i8(msg?).ok_or(Error::UnrecognizedPacket)?; match msg { - Request::GetLog => { - let buffer = get_logger_buffer().await.extract().as_bytes().to_vec(); - write_i8(stream, Reply::LogContent as i8).await?; - write_chunk(stream, &buffer).await?; - } - Request::ClearLog => { - let mut buffer = get_logger_buffer().await; - buffer.clear(); - write_i8(stream, Reply::Success as i8).await?; - } - Request::PullLog => { - let id = { - let mut guard = pull_id.borrow_mut(); - *guard += 1; - *guard - }; - loop { - let mut buffer = get_logger_buffer_pred(|b| !b.is_empty()).await; - if id != *pull_id.borrow() { - // another connection attempts to pull the log... - // abort this connection... - break; - } - let bytes = buffer.extract().as_bytes().to_vec(); - buffer.clear(); - core::mem::drop(buffer); - write_chunk(stream, &bytes).await?; - if log::max_level() == LevelFilter::Trace { - // temporarily discard all trace level log - let logger = unsafe { BufferLogger::get_logger().as_mut().unwrap() }; - logger.set_buffer_log_level(LevelFilter::Debug); - stream.flush().await?; - logger.set_buffer_log_level(LevelFilter::Trace); - } - } - } + Request::GetLog => process!(stream, _drtio_tuple, _destination, get_log), + Request::ClearLog => process!(stream, _drtio_tuple, _destination, clear_log), + Request::PullLog => process!(stream, _drtio_tuple, _destination, pull_log, &pull_id), Request::SetLogFilter => { let lvl = read_log_level_filter(stream).await?; - info!("Changing log level to {}", lvl); - log::set_max_level(lvl); - write_i8(stream, Reply::Success as i8).await?; + process!(stream, _drtio_tuple, _destination, set_log_filter, lvl) } Request::SetUartLogFilter => { let lvl = read_log_level_filter(stream).await?; - info!("Changing UART log level to {}", lvl); - unsafe { - BufferLogger::get_logger().as_ref().unwrap().set_uart_log_level(lvl); - } - write_i8(stream, Reply::Success as i8).await?; + process!(stream, _drtio_tuple, _destination, set_uart_log_filter, lvl) } Request::ConfigRead => { let key = read_key(stream).await?; - debug!("read key: {}", key); - let value = cfg.read(&key); - if let Ok(value) = value { - debug!("got value"); - write_i8(stream, Reply::ConfigData as i8).await?; - write_chunk(stream, &value).await?; - } else { - warn!("read error: no such key"); - write_i8(stream, Reply::Error as i8).await?; - } + process!(stream, _drtio_tuple, _destination, config_read, &cfg, &key) } Request::ConfigWrite => { let key = read_key(stream).await?; - debug!("write key: {}", key); let len = read_i32(stream).await?; let len = if len <= 0 { 0 } else { len as usize }; let mut buffer = Vec::with_capacity(len); @@ -203,45 +909,56 @@ async fn handle_connection( buffer.set_len(len); } read_chunk(stream, &mut buffer).await?; - let value = cfg.write(&key, buffer); - if value.is_ok() { - debug!("write success"); - if key == "idle_kernel" { - restart_idle.signal(); - } - write_i8(stream, Reply::Success as i8).await?; - } else { - // this is an error because we do not expect write to fail - error!("failed to write: {:?}", value); - write_i8(stream, Reply::Error as i8).await?; - } + process!( + stream, + _drtio_tuple, + _destination, + config_write, + &cfg, + &key, + buffer, + &restart_idle + ) } Request::ConfigRemove => { let key = read_key(stream).await?; - debug!("erase key: {}", key); - let value = cfg.remove(&key); - if value.is_ok() { - debug!("erase success"); - if key == "idle_kernel" { - restart_idle.signal(); - } - write_i8(stream, Reply::Success as i8).await?; - } else { - warn!("erase failed"); - write_i8(stream, Reply::Error as i8).await?; - } + process!( + stream, + _drtio_tuple, + _destination, + config_remove, + &cfg, + &key, + &restart_idle + ) } Request::Reboot => { - info!("rebooting"); - write_i8(stream, Reply::RebootImminent as i8).await?; - stream.flush().await?; - slcr::reboot(); + process!(stream, _drtio_tuple, _destination, reboot) } - } + Request::ConfigErase => { + process!(stream, _drtio_tuple, _destination, config_erase) + } + Request::DebugAllocator => { + process!(stream, _drtio_tuple, _destination, debug_allocator) + } + Request::Flash => { + let len = read_i32(stream).await?; + if len <= 0 { + write_i8(stream, Reply::Error as i8).await?; + return Err(Error::UnexpectedPattern); + } + let mut buffer = Vec::with_capacity(len as usize); + unsafe { + buffer.set_len(len as usize); + } + read_chunk(stream, &mut buffer).await?; + process!(stream, _drtio_tuple, _destination, image_write, &cfg, buffer) + } + }?; } } -pub fn start(cfg: Rc, restart_idle: Rc) { +pub fn start(cfg: Rc, restart_idle: Rc, drtio_tuple: Option) { task::spawn(async move { let pull_id = Rc::new(RefCell::new(0u32)); loop { @@ -249,9 +966,10 @@ pub fn start(cfg: Rc, restart_idle: Rc) { let pull_id = pull_id.clone(); let cfg = cfg.clone(); let restart_idle = restart_idle.clone(); + let drtio_tuple = drtio_tuple.clone(); task::spawn(async move { info!("received connection"); - let _ = handle_connection(&mut stream, pull_id, cfg, restart_idle) + let _ = handle_connection(&mut stream, pull_id, cfg, restart_idle, drtio_tuple) .await .map_err(|e| warn!("connection terminated: {:?}", e)); let _ = stream.flush().await; diff --git a/src/runtime/src/rtio_mgt.rs b/src/runtime/src/rtio_mgt.rs index 53a3b8b..4fa8cce 100644 --- a/src/runtime/src/rtio_mgt.rs +++ b/src/runtime/src/rtio_mgt.rs @@ -540,7 +540,7 @@ pub mod drtio { } } - async fn partition_data( + pub async fn partition_data( linkno: u8, aux_mutex: &Rc>, routing_table: &RoutingTable, diff --git a/src/satman/Cargo.toml.tpl b/src/satman/Cargo.toml.tpl index d4fa63c..38f64e6 100644 --- a/src/satman/Cargo.toml.tpl +++ b/src/satman/Cargo.toml.tpl @@ -15,7 +15,9 @@ build_zynq = { path = "../libbuild_zynq" } [dependencies] log = { version = "0.4", default-features = false } +byteorder = { version = "1.3", default-features = false } core_io = { version = "0.1", features = ["collections"] } +crc = { version = "1.7", default-features = false } cslice = "0.3" embedded-hal = "0.2" diff --git a/src/satman/src/main.rs b/src/satman/src/main.rs index aebd9d6..d610c46 100644 --- a/src/satman/src/main.rs +++ b/src/satman/src/main.rs @@ -4,7 +4,9 @@ #[macro_use] extern crate log; +extern crate byteorder; extern crate core_io; +extern crate crc; extern crate cslice; extern crate embedded_hal; @@ -38,16 +40,18 @@ use libboard_artiq::{drtio_routing, drtioaux, pl::csr}; #[cfg(feature = "target_kasli_soc")] use libboard_zynq::error_led::ErrorLED; -use libboard_zynq::{i2c::I2c, print, println, time::Milliseconds, timer::GlobalTimer}; +use libboard_zynq::{i2c::I2c, print, println, slcr, time::Milliseconds, timer::GlobalTimer}; use libconfig::Config; use libcortex_a9::{l2c::enable_l2_cache, regs::MPIDR}; use libregister::RegisterR; use libsupport_zynq::{exception_vectors, ram}; +use mgmt::Manager as CoreManager; use routing::Router; use subkernel::Manager as KernelManager; mod analyzer; mod dma; +mod mgmt; mod repeater; mod routing; mod subkernel; @@ -149,6 +153,7 @@ fn process_aux_packet( dma_manager: &mut DmaManager, analyzer: &mut Analyzer, kernel_manager: &mut KernelManager, + core_manager: &mut CoreManager, router: &mut Router, ) -> Result<(), drtioaux::Error> { // In the code below, *_chan_sel_write takes an u8 if there are fewer than 256 channels, @@ -1010,6 +1015,317 @@ fn process_aux_packet( } Ok(()) } + drtioaux::Packet::CoreMgmtGetLogRequest { + destination: _destination, + clear, + } => { + forward!( + router, + _routing_table, + _destination, + *rank, + *self_destination, + _repeaters, + &packet, + timer + ); + let mut data_slice = [0; SAT_PAYLOAD_MAX_SIZE]; + let meta = core_manager.log_get_slice(&mut data_slice, clear); + drtioaux::send( + 0, + &drtioaux::Packet::CoreMgmtGetLogReply { + last: meta.status.is_last(), + length: meta.len as u16, + data: data_slice, + }, + ) + } + drtioaux::Packet::CoreMgmtClearLogRequest { + destination: _destination, + } => { + forward!( + router, + _routing_table, + _destination, + *rank, + *self_destination, + _repeaters, + &packet, + timer + ); + mgmt::clear_log(); + drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true }) + } + drtioaux::Packet::CoreMgmtSetLogLevelRequest { + destination: _destination, + log_level, + } => { + forward!( + router, + _routing_table, + _destination, + *rank, + *self_destination, + _repeaters, + &packet, + timer + ); + + if let Ok(level_filter) = mgmt::byte_to_level_filter(log_level) { + info!("Changing log level to {}", level_filter); + log::set_max_level(level_filter); + drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true }) + } else { + drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false }) + } + } + drtioaux::Packet::CoreMgmtSetUartLogLevelRequest { + destination: _destination, + log_level, + } => { + forward!( + router, + _routing_table, + _destination, + *rank, + *self_destination, + _repeaters, + &packet, + timer + ); + + if let Ok(level_filter) = mgmt::byte_to_level_filter(log_level) { + info!("Changing UART log level to {}", level_filter); + unsafe { + logger::BufferLogger::get_logger() + .as_ref() + .unwrap() + .set_uart_log_level(level_filter); + } + drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true }) + } else { + drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false }) + } + } + drtioaux::Packet::CoreMgmtConfigReadRequest { + destination: _destination, + length, + key, + } => { + forward!( + router, + _routing_table, + _destination, + *rank, + *self_destination, + _repeaters, + &packet, + timer + ); + + let mut value_slice = [0; SAT_PAYLOAD_MAX_SIZE]; + + let key_slice = &key[..length as usize]; + if !key_slice.is_ascii() { + error!("invalid key"); + drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false }) + } else { + let key = core::str::from_utf8(key_slice).unwrap(); + if core_manager.fetch_config_value(key).is_ok() { + let meta = core_manager.get_config_value_slice(&mut value_slice); + drtioaux::send( + 0, + &drtioaux::Packet::CoreMgmtConfigReadReply { + last: meta.status.is_last(), + length: meta.len as u16, + value: value_slice, + }, + ) + } else { + drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false }) + } + } + } + drtioaux::Packet::CoreMgmtConfigReadContinue { + destination: _destination, + } => { + forward!( + router, + _routing_table, + _destination, + *rank, + *self_destination, + _repeaters, + &packet, + timer + ); + + let mut value_slice = [0; SAT_PAYLOAD_MAX_SIZE]; + let meta = core_manager.get_config_value_slice(&mut value_slice); + drtioaux::send( + 0, + &drtioaux::Packet::CoreMgmtConfigReadReply { + last: meta.status.is_last(), + length: meta.len as u16, + value: value_slice, + }, + ) + } + drtioaux::Packet::CoreMgmtConfigWriteRequest { + destination: _destination, + last, + length, + data, + } => { + forward!( + router, + _routing_table, + _destination, + *rank, + *self_destination, + _repeaters, + &packet, + timer + ); + + core_manager.add_config_data(&data, length as usize); + + let mut succeeded = true; + if last { + succeeded = core_manager.write_config().is_ok(); + core_manager.clear_config_data(); + } + + drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded }) + } + drtioaux::Packet::CoreMgmtConfigRemoveRequest { + destination: _destination, + length, + key, + } => { + forward!( + router, + _routing_table, + _destination, + *rank, + *self_destination, + _repeaters, + &packet, + timer + ); + + let key_slice = &key[..length as usize]; + if !key_slice.is_ascii() { + error!("invalid key"); + drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false }) + } else { + let key = core::str::from_utf8(key_slice).unwrap(); + let succeeded = core_manager.remove_config(key).is_ok(); + drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded }) + } + } + drtioaux::Packet::CoreMgmtConfigEraseRequest { + destination: _destination, + } => { + forward!( + router, + _routing_table, + _destination, + *rank, + *self_destination, + _repeaters, + &packet, + timer + ); + + error!("config erase not supported on zynq device"); + drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false }) + } + drtioaux::Packet::CoreMgmtRebootRequest { + destination: _destination, + } => { + info!("received reboot request"); + forward!( + router, + _routing_table, + _destination, + *rank, + *self_destination, + _repeaters, + &packet, + timer + ); + + drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true })?; + info!("reboot imminent"); + slcr::reboot(); + + unreachable!(); + } + drtioaux::Packet::CoreMgmtAllocatorDebugRequest { + destination: _destination, + } => { + forward!( + router, + _routing_table, + _destination, + *rank, + *self_destination, + _repeaters, + &packet, + timer + ); + + error!("debug allocator not supported on zynq device"); + drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: false }) + } + drtioaux::Packet::CoreMgmtFlashRequest { + destination: _destination, + last, + length, + data, + } => { + forward!( + router, + _routing_table, + _destination, + *rank, + *self_destination, + _repeaters, + &packet, + timer + ); + + core_manager.add_image_data(&data, length as usize); + + if last { + drtioaux::send(0, &drtioaux::Packet::CoreMgmtDropLink) + } else { + drtioaux::send(0, &drtioaux::Packet::CoreMgmtReply { succeeded: true }) + } + } + drtioaux::Packet::CoreMgmtDropLinkAck { + destination: _destination, + } => { + forward!( + router, + _routing_table, + _destination, + *rank, + *self_destination, + _repeaters, + &packet, + timer + ); + + unsafe { + csr::gt_drtio::txenable_write(0); + } + core_manager.write_image(); + info!("reboot imminent"); + slcr::reboot(); + Ok(()) + } p => { warn!("received unexpected aux packet: {:?}", p); @@ -1028,6 +1344,7 @@ fn process_aux_packets( dma_manager: &mut DmaManager, analyzer: &mut Analyzer, kernel_manager: &mut KernelManager, + core_manager: &mut CoreManager, router: &mut Router, ) { let result = drtioaux::recv(0).and_then(|packet| { @@ -1043,6 +1360,7 @@ fn process_aux_packets( dma_manager, analyzer, kernel_manager, + core_manager, router, ) } else { @@ -1239,7 +1557,7 @@ pub extern "C" fn main_core0() -> i32 { #[cfg(has_si549)] si549::helper_setup(&mut timer, &SI549_SETTINGS).expect("cannot initialize helper Si549"); - let cfg = match Config::new() { + let mut cfg = match Config::new() { Ok(cfg) => cfg, Err(err) => { warn!("config initialization failed: {}", err); @@ -1314,6 +1632,7 @@ pub extern "C" fn main_core0() -> i32 { let mut dma_manager = DmaManager::new(); let mut analyzer = Analyzer::new(); let mut kernel_manager = KernelManager::new(&mut control); + let mut core_manager = CoreManager::new(&mut cfg); drtioaux::reset(0); drtiosat_reset(false); @@ -1331,6 +1650,7 @@ pub extern "C" fn main_core0() -> i32 { &mut dma_manager, &mut analyzer, &mut kernel_manager, + &mut core_manager, &mut router, ); #[allow(unused_mut)] diff --git a/src/satman/src/mgmt.rs b/src/satman/src/mgmt.rs new file mode 100644 index 0000000..80ca733 --- /dev/null +++ b/src/satman/src/mgmt.rs @@ -0,0 +1,145 @@ +use alloc::vec::Vec; + +use byteorder::{ByteOrder, NativeEndian}; +use crc::crc32; +use io::{ProtoRead, ProtoWrite}; +use libboard_artiq::{drtioaux_proto::SAT_PAYLOAD_MAX_SIZE, + logger::{BufferLogger, LogBufferRef}}; +use libconfig::Config; +use log::{debug, error, info, warn, LevelFilter}; + +use crate::routing::{SliceMeta, Sliceable}; + +type Result = core::result::Result; + +pub fn byte_to_level_filter(level_byte: u8) -> Result { + Ok(match level_byte { + 0 => LevelFilter::Off, + 1 => LevelFilter::Error, + 2 => LevelFilter::Warn, + 3 => LevelFilter::Info, + 4 => LevelFilter::Debug, + 5 => LevelFilter::Trace, + lv => { + error!("unknown log level: {}", lv); + return Err(()); + } + }) +} + +fn get_logger_buffer() -> LogBufferRef<'static> { + let logger = unsafe { BufferLogger::get_logger().as_mut().unwrap() }; + loop { + if let Some(buffer_ref) = logger.buffer() { + return buffer_ref; + } + } +} + +pub fn clear_log() { + let mut buffer = get_logger_buffer(); + buffer.clear(); +} + +pub struct Manager<'a> { + cfg: &'a mut Config, + last_log: Sliceable, + config_payload: Vec, + last_value: Sliceable, + image_payload: Vec, +} + +impl<'a> Manager<'_> { + pub fn new(cfg: &mut Config) -> Manager { + Manager { + cfg: cfg, + last_log: Sliceable::new(0, Vec::new()), + config_payload: Vec::new(), + last_value: Sliceable::new(0, Vec::new()), + image_payload: Vec::new(), + } + } + + pub fn log_get_slice(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE], consume: bool) -> SliceMeta { + // Populate buffer if depleted + if self.last_log.at_end() { + let mut buffer = get_logger_buffer(); + self.last_log.extend(buffer.extract().as_bytes()); + if consume { + buffer.clear(); + } + } + + self.last_log.get_slice_satellite(data_slice) + } + + pub fn fetch_config_value(&mut self, key: &str) -> Result<()> { + self.cfg + .read(&key) + .map(|value| { + debug!("got value"); + self.last_value = Sliceable::new(0, value) + }) + .map_err(|_| warn!("read error: no such key")) + } + + pub fn get_config_value_slice(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE]) -> SliceMeta { + self.last_value.get_slice_satellite(data_slice) + } + + pub fn add_config_data(&mut self, data: &[u8], data_len: usize) { + self.config_payload.write_all(&data[..data_len]).unwrap(); + } + + pub fn clear_config_data(&mut self) { + self.config_payload.clear(); + } + + pub fn write_config(&mut self) -> Result<()> { + let mut payload = &self.config_payload[..]; + let key = payload.read_string().map_err(|_err| error!("error on reading key"))?; + debug!("write key: {}", key); + let value = payload.read_bytes().unwrap(); + + self.cfg + .write(&key, value) + .map(|()| debug!("write success")) + .map_err(|err| error!("failed to write: {:?}", err)) + } + + pub fn remove_config(&mut self, key: &str) -> Result<()> { + debug!("erase key: {}", key); + self.cfg + .remove(&key) + .map(|()| debug!("erase success")) + .map_err(|err| warn!("failed to erase: {:?}", err)) + } + + pub fn add_image_data(&mut self, data: &[u8], data_len: usize) { + self.image_payload.extend(&data[..data_len]); + } + + pub fn write_image(&self) { + let mut image = self.image_payload.clone(); + let image_ref = &image[..]; + let bin_len = image.len() - 4; + + let (image_ref, expected_crc) = { + let (image_ref, crc_slice) = image_ref.split_at(bin_len); + (image_ref, NativeEndian::read_u32(crc_slice)) + }; + + let actual_crc = crc32::checksum_ieee(image_ref); + + if actual_crc == expected_crc { + info!("CRC passed. Writing boot image to SD card..."); + image.truncate(bin_len); + self.cfg.write("boot", image).expect("failed to write boot image"); + } else { + panic!( + "CRC failed in SDRAM (actual {:08x}, expected {:08x})", + actual_crc, expected_crc + ); + } + } +} diff --git a/src/satman/src/routing.rs b/src/satman/src/routing.rs index 87d5f09..2ea1ac5 100644 --- a/src/satman/src/routing.rs +++ b/src/satman/src/routing.rs @@ -4,7 +4,7 @@ use core::cmp::min; #[cfg(has_drtio_routing)] use libboard_artiq::pl::csr; use libboard_artiq::{drtio_routing, drtioaux, - drtioaux_proto::{PayloadStatus, MASTER_PAYLOAD_MAX_SIZE}}; + drtioaux_proto::{PayloadStatus, MASTER_PAYLOAD_MAX_SIZE, SAT_PAYLOAD_MAX_SIZE}}; pub struct SliceMeta { pub destination: u8, @@ -58,6 +58,7 @@ impl Sliceable { } get_slice_fn!(get_slice_master, MASTER_PAYLOAD_MAX_SIZE); + get_slice_fn!(get_slice_satellite, SAT_PAYLOAD_MAX_SIZE); } // Packets from downstream (further satellites) are received and routed appropriately.