diff --git a/src/satman/src/main.rs b/src/satman/src/main.rs index 6db698c..336a592 100644 --- a/src/satman/src/main.rs +++ b/src/satman/src/main.rs @@ -38,16 +38,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 +151,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, @@ -1011,6 +1014,279 @@ 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); + if clear && meta.status.is_first() { + mgmt::clear_log(); + } + 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::CoreMgmtAck) + } + 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::CoreMgmtAck) + } else { + drtioaux::send(0, &drtioaux::Packet::CoreMgmtNack) + } + } + 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 log level to {}", level_filter); + unsafe { + logger::BufferLogger::get_logger() + .as_ref() + .unwrap() + .set_uart_log_level(level_filter); + } + drtioaux::send(0, &drtioaux::Packet::CoreMgmtAck) + } else { + drtioaux::send(0, &drtioaux::Packet::CoreMgmtNack) + } + } + 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::CoreMgmtNack) + } 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::CoreMgmtNack) + } + } + } + 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_data(&data, length as usize); + + let mut succeeded = true; + if last { + succeeded = core_manager.write_config().is_ok(); + core_manager.clear_data(); + } + + if succeeded { + drtioaux::send(0, &drtioaux::Packet::CoreMgmtAck) + } else { + drtioaux::send(0, &drtioaux::Packet::CoreMgmtNack) + } + } + 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::CoreMgmtNack) + } else { + let key = core::str::from_utf8(key_slice).unwrap(); + if core_manager.remove_config(key).is_ok() { + drtioaux::send(0, &drtioaux::Packet::CoreMgmtAck) + } else { + drtioaux::send(0, &drtioaux::Packet::CoreMgmtNack) + } + } + } + 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::CoreMgmtNack) + } + 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::CoreMgmtAck)?; + info!("reboot imminent"); + slcr::reboot(); + Ok(()) + } + 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::CoreMgmtNack) + } p => { warn!("received unexpected aux packet: {:?}", p); @@ -1029,6 +1305,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| { @@ -1044,6 +1321,7 @@ fn process_aux_packets( dma_manager, analyzer, kernel_manager, + core_manager, router, ) } else { @@ -1240,7 +1518,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); @@ -1315,6 +1593,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); @@ -1332,6 +1611,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..169037e --- /dev/null +++ b/src/satman/src/mgmt.rs @@ -0,0 +1,116 @@ +use alloc::vec::Vec; + +use io::{Cursor, ProtoRead, ProtoWrite}; +use libboard_artiq::{drtioaux_proto::SAT_PAYLOAD_MAX_SIZE, + logger::{BufferLogger, LogBufferRef}}; +use libconfig::Config; +use log::{self, 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 => log::LevelFilter::Off, + 1 => log::LevelFilter::Error, + 2 => log::LevelFilter::Warn, + 3 => log::LevelFilter::Info, + 4 => log::LevelFilter::Debug, + 5 => log::LevelFilter::Trace, + lv => { + error!("unknown log level: {}", lv); + return Err(()); + } + }) +} + +fn get_logger_buffer_pred() -> LogBufferRef<'static> { + let logger = unsafe { BufferLogger::get_logger().as_mut().unwrap() }; + loop { + if let Some(buffer_ref) = logger.buffer() { + return buffer_ref; + } + } +} + +fn get_logger_buffer() -> LogBufferRef<'static> { + get_logger_buffer_pred() +} + +pub fn clear_log() { + let mut buffer = get_logger_buffer(); + buffer.clear(); +} + +pub struct Manager<'a> { + cfg: &'a mut Config, + last_log: Sliceable, + current_payload: Cursor>, + last_value: Sliceable, +} + +impl<'a> Manager<'_> { + pub fn new(cfg: &mut Config) -> Manager { + Manager { + cfg: cfg, + last_log: Sliceable::new(0, Vec::new()), + current_payload: Cursor::new(Vec::new()), + last_value: Sliceable::new(0, Vec::new()), + } + } + + pub fn log_get_slice(&mut self, data_slice: &mut [u8; SAT_PAYLOAD_MAX_SIZE]) -> SliceMeta { + // Populate buffer if depleted + if self.last_log.at_end() { + self.last_log.extend(get_logger_buffer().extract().as_bytes()); + } + + 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_data(&mut self, data: &[u8], data_len: usize) { + self.current_payload.write_all(&data[..data_len]).unwrap(); + } + + pub fn clear_data(&mut self) { + self.current_payload.get_mut().clear(); + self.current_payload.set_position(0); + } + + pub fn write_config(&mut self) -> Result<()> { + let key = self + .current_payload + .read_string() + .map_err(|_err| error!("error on reading key"))?; + debug!("write key: {}", key); + let value = self.current_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)) + } +} 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.