diff --git a/artiq/firmware/ksupport/lib.rs b/artiq/firmware/ksupport/lib.rs index 42f9de3e5..4c629b22a 100644 --- a/artiq/firmware/ksupport/lib.rs +++ b/artiq/firmware/ksupport/lib.rs @@ -494,7 +494,8 @@ extern "C-unwind" fn subkernel_await_finish(id: u32, timeout: i64) { SubkernelStatus::CommLost => raise!("SubkernelError", "Lost communication with satellite"), SubkernelStatus::OtherError => raise!("SubkernelError", - "An error occurred during subkernel operation") + "An error occurred during subkernel operation"), + SubkernelStatus::Exception(e) => unsafe { crate::eh_artiq::raise(e) }, } }) } @@ -528,7 +529,8 @@ extern "C-unwind" fn subkernel_await_message(id: i32, timeout: i64, tags: &CSlic SubkernelStatus::CommLost => raise!("SubkernelError", "Lost communication with satellite"), SubkernelStatus::OtherError => raise!("SubkernelError", - "An error occurred during subkernel operation") + "An error occurred during subkernel operation"), + SubkernelStatus::Exception(e) => unsafe { crate::eh_artiq::raise(e) }, } }) // RpcRecvRequest should be called `count` times after this to receive message data diff --git a/artiq/firmware/libproto_artiq/kernel_proto.rs b/artiq/firmware/libproto_artiq/kernel_proto.rs index 1a2f057b7..a9ff0eac3 100644 --- a/artiq/firmware/libproto_artiq/kernel_proto.rs +++ b/artiq/firmware/libproto_artiq/kernel_proto.rs @@ -11,12 +11,13 @@ pub const KERNELCPU_LAST_ADDRESS: usize = 0x4fffffff; pub const KSUPPORT_HEADER_SIZE: usize = 0x74; #[derive(Debug)] -pub enum SubkernelStatus { +pub enum SubkernelStatus<'a> { NoError, Timeout, IncorrectState, CommLost, - OtherError + OtherError, + Exception(eh::eh_artiq::Exception<'a>), } #[derive(Debug)] @@ -106,10 +107,10 @@ pub enum Message<'a> { SubkernelLoadRunRequest { id: u32, destination: u8, run: bool }, SubkernelLoadRunReply { succeeded: bool }, SubkernelAwaitFinishRequest { id: u32, timeout: i64 }, - SubkernelAwaitFinishReply { status: SubkernelStatus }, + SubkernelAwaitFinishReply { status: SubkernelStatus<'a> }, SubkernelMsgSend { id: u32, destination: Option, count: u8, tag: &'a [u8], data: *const *const () }, SubkernelMsgRecvRequest { id: i32, timeout: i64, tags: &'a [u8] }, - SubkernelMsgRecvReply { status: SubkernelStatus, count: u8 }, + SubkernelMsgRecvReply { status: SubkernelStatus<'a>, count: u8 }, Log(fmt::Arguments<'a>), LogSlice(&'a str) diff --git a/artiq/firmware/runtime/kernel.rs b/artiq/firmware/runtime/kernel.rs index dda190cd9..9db3bba9e 100644 --- a/artiq/firmware/runtime/kernel.rs +++ b/artiq/firmware/runtime/kernel.rs @@ -95,7 +95,9 @@ pub mod subkernel { use board_artiq::drtio_routing::RoutingTable; use board_misoc::clock; use proto_artiq::{drtioaux_proto::{PayloadStatus, MASTER_PAYLOAD_MAX_SIZE}, rpc_proto as rpc}; - use io::Cursor; + use io::{Cursor, ProtoRead}; + use eh::eh_artiq::Exception; + use cslice::CSlice; use rtio_mgt::drtio; use sched::{Io, Mutex, Error as SchedError}; @@ -226,8 +228,8 @@ pub mod subkernel { if subkernel.state == SubkernelState::Running { subkernel.state = SubkernelState::Finished { status: match with_exception { - true => FinishStatus::Exception(exception_src), - false => FinishStatus::Ok, + true => FinishStatus::Exception(exception_src), + false => FinishStatus::Ok, } } } @@ -256,6 +258,42 @@ pub mod subkernel { } } + fn read_exception_string<'a>(reader: &mut Cursor<&[u8]>) -> Result, Error> { + let len = reader.read_u32()? as usize; + if len == usize::MAX { + let data = reader.read_u32()?; + Ok(unsafe { CSlice::new(data as *const u8, len) }) + } else { + let pos = reader.position(); + let slice = unsafe { + let ptr = reader.get_ref().as_ptr().offset(pos as isize); + CSlice::new(ptr, len) + }; + reader.set_position(pos + len); + Ok(slice) + } + } + + pub fn read_exception(buffer: &[u8]) -> Result + { + let mut reader = Cursor::new(buffer); + + let _sync = reader.read_u32()?; + let _9 = reader.read_u8()?; + let _len = reader.read_u32()?; + // ignore the remaining exceptions, stack traces etc. - unwinding from another device would be unwise anyway + Ok(Exception { + id: reader.read_u32()?, + message: read_exception_string(&mut reader)?, + param: [reader.read_u64()? as i64, reader.read_u64()? as i64, reader.read_u64()? as i64], + file: read_exception_string(&mut reader)?, + line: reader.read_u32()?, + column: reader.read_u32()?, + function: read_exception_string(&mut reader)? + }) + } + + pub fn retrieve_finish_status(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, subkernel_mutex: &Mutex, routing_table: &RoutingTable, id: u32) -> Result { let _lock = subkernel_mutex.lock(io)?; diff --git a/artiq/firmware/runtime/session.rs b/artiq/firmware/runtime/session.rs index d972414bd..4f27c7b74 100644 --- a/artiq/firmware/runtime/session.rs +++ b/artiq/firmware/runtime/session.rs @@ -126,19 +126,6 @@ macro_rules! unexpected { ($($arg:tt)*) => (return Err(Error::Unexpected(format!($($arg)*)))); } -#[cfg(has_drtio)] -macro_rules! propagate_subkernel_exception { - ( $exception:ident, $stream:ident ) => { - error!("Exception in subkernel"); - match $stream { - None => return Ok(true), - Some(ref mut $stream) => { - $stream.write_all($exception)?; - } - } - } -} - // Persistent state #[derive(Debug)] struct Congress { @@ -690,10 +677,13 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex, Ok(ref res) => { if res.comm_lost { kern::SubkernelStatus::CommLost - } else if let Some(exception) = &res.exception { - propagate_subkernel_exception!(exception, stream); - // will not be called after exception is served - kern::SubkernelStatus::OtherError + } else if let Some(raw_exception) = &res.exception { + let exception = subkernel::read_exception(raw_exception); + if let Ok(exception) = exception { + kern::SubkernelStatus::Exception(exception) + } else { + kern::SubkernelStatus::OtherError + } } else { kern::SubkernelStatus::NoError } @@ -712,72 +702,92 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex, #[cfg(has_drtio)] &kern::SubkernelMsgRecvRequest { id, timeout, tags } => { let message_received = subkernel::message_await(io, subkernel_mutex, id as u32, timeout); - let (status, count) = match message_received { - Ok(ref message) => (kern::SubkernelStatus::NoError, message.count), - Err(SubkernelError::Timeout) => (kern::SubkernelStatus::Timeout, 0), - Err(SubkernelError::IncorrectState) => (kern::SubkernelStatus::IncorrectState, 0), - Err(SubkernelError::SubkernelFinished) => { - let res = subkernel::retrieve_finish_status(io, aux_mutex, ddma_mutex, subkernel_mutex, - routing_table, id as u32)?; - if res.comm_lost { - (kern::SubkernelStatus::CommLost, 0) - } else if let Some(exception) = &res.exception { - propagate_subkernel_exception!(exception, stream); - (kern::SubkernelStatus::OtherError, 0) - } else { - (kern::SubkernelStatus::OtherError, 0) - } - } - Err(_) => (kern::SubkernelStatus::OtherError, 0) - }; - kern_send(io, &kern::SubkernelMsgRecvReply { status: status, count: count})?; - if let Ok(message) = message_received { - // receive code almost identical to RPC recv, except we are not reading from a stream - let mut reader = Cursor::new(message.data); - let mut current_tags = tags; - let mut i = 0; - loop { - // kernel has to consume all arguments in the whole message - let slot = kern_recv(io, |reply| { - match reply { - &kern::RpcRecvRequest(slot) => Ok(slot), - other => unexpected!( - "expected root value slot from kernel CPU, not {:?}", other) - } + if let Err(SubkernelError::SubkernelFinished) = message_received { + let res = subkernel::retrieve_finish_status(io, aux_mutex, ddma_mutex, subkernel_mutex, + routing_table, id as u32)?; + if res.comm_lost { + kern_send(io, + &kern::SubkernelMsgRecvReply { + status: kern::SubkernelStatus::CommLost, + count: 0 })?; - let res = rpc::recv_return(&mut reader, current_tags, slot, &|size| -> Result<_, Error> { - if size == 0 { - return Ok(0 as *mut ()) - } - kern_send(io, &kern::RpcRecvReply(Ok(size)))?; - Ok(kern_recv(io, |reply| { + } else if let Some(raw_exception) = &res.exception { + let exception = subkernel::read_exception(raw_exception); + if let Ok(exception) = exception { + kern_send(io, + &kern::SubkernelMsgRecvReply { + status: kern::SubkernelStatus::Exception(exception), + count: 0 + })?; + } else { + kern_send(io, + &kern::SubkernelMsgRecvReply { + status: kern::SubkernelStatus::OtherError, + count: 0 + })?; + } + } else { + kern_send(io, + &kern::SubkernelMsgRecvReply { + status: kern::SubkernelStatus::OtherError, + count: 0 + })?; + } + } else { + let (status, count) = match message_received { + Ok(ref message) => (kern::SubkernelStatus::NoError, message.count), + Err(SubkernelError::Timeout) => (kern::SubkernelStatus::Timeout, 0), + Err(SubkernelError::IncorrectState) => (kern::SubkernelStatus::IncorrectState, 0), + Err(SubkernelError::SubkernelFinished) => unreachable!(), // taken care of above + Err(_) => (kern::SubkernelStatus::OtherError, 0) + }; + kern_send(io, &kern::SubkernelMsgRecvReply { status: status, count: count})?; + if let Ok(message) = message_received { + // receive code almost identical to RPC recv, except we are not reading from a stream + let mut reader = Cursor::new(message.data); + let mut current_tags = tags; + let mut i = 0; + loop { + // kernel has to consume all arguments in the whole message + let slot = kern_recv(io, |reply| { match reply { &kern::RpcRecvRequest(slot) => Ok(slot), other => unexpected!( - "expected nested value slot from kernel CPU, not {:?}", other) + "expected root value slot from kernel CPU, not {:?}", other) } - })?) - }); - match res { - Ok(new_tags) => { - kern_send(io, &kern::RpcRecvReply(Ok(0)))?; - i += 1; - if i < message.count { - // update the tag for next read - current_tags = new_tags; - } else { - // should be done by then - break; + })?; + let res = rpc::recv_return(&mut reader, current_tags, slot, &|size| -> Result<_, Error> { + if size == 0 { + return Ok(0 as *mut ()) } - }, - Err(_) => unexpected!("expected valid subkernel message data") - }; + kern_send(io, &kern::RpcRecvReply(Ok(size)))?; + Ok(kern_recv(io, |reply| { + match reply { + &kern::RpcRecvRequest(slot) => Ok(slot), + other => unexpected!( + "expected nested value slot from kernel CPU, not {:?}", other) + } + })?) + }); + match res { + Ok(new_tags) => { + kern_send(io, &kern::RpcRecvReply(Ok(0)))?; + i += 1; + if i < message.count { + // update the tag for next read + current_tags = new_tags; + } else { + // should be done by then + break; + } + }, + Err(_) => unexpected!("expected valid subkernel message data") + }; + } } - Ok(()) - } else { // if timed out, no data has been received, exception should be raised by kernel - Ok(()) } + Ok(()) }, request => unexpected!("unexpected request {:?} from kernel CPU", request)