diff --git a/emulation/example.svd b/emulation/renode.svd similarity index 100% rename from emulation/example.svd rename to emulation/renode.svd diff --git a/kernel/src/arch/hosted.rs b/kernel/src/arch/hosted.rs index b9e56b87c..687303163 100644 --- a/kernel/src/arch/hosted.rs +++ b/kernel/src/arch/hosted.rs @@ -68,6 +68,10 @@ fn generate_pid_key() -> [u8; 16] { process_key } +pub fn current_pid() -> PID { + crate::arch::process::current_pid() +} + /// Each client gets its own connection and its own thread, which is handled here. fn handle_connection( conn: TcpStream, diff --git a/kernel/src/arch/hosted/process.rs b/kernel/src/arch/hosted/process.rs index 3ac462e2f..738e384f2 100644 --- a/kernel/src/arch/hosted/process.rs +++ b/kernel/src/arch/hosted/process.rs @@ -171,6 +171,19 @@ impl Process { }) } + /// Calls the provided function with the current inner process state. + pub fn with_current_mut(f: F) -> R + where + F: FnOnce(&mut Process) -> R, + { + let mut process = Self::current(); + f(&mut process) + } + + pub fn current_tid(&self) -> TID { + 1 + } + fn setup_thread_inner(thread: TID, process_table: &mut ProcessTable) { let current_pid_idx = process_table.current.get() as usize - 1; let process = &mut process_table.table[current_pid_idx].as_mut().unwrap(); @@ -179,6 +192,10 @@ impl Process { process.threads[thread - 1].allocated = true; } + pub fn retry_instruction(&mut self, _tid: TID) -> Result<(), xous_kernel::Error> { + Ok(()) + } + pub fn setup_process(pid: PID, setup: ThreadInit) -> Result<(), xous_kernel::Error> { let mut tmp = Process { pid }; tmp.setup_thread(INITIAL_TID, setup) diff --git a/kernel/src/arch/riscv/process.rs b/kernel/src/arch/riscv/process.rs index 5a5d8e04c..c4f439809 100644 --- a/kernel/src/arch/riscv/process.rs +++ b/kernel/src/arch/riscv/process.rs @@ -235,6 +235,15 @@ impl Process { } } + pub fn retry_instruction(&mut self, tid: TID) -> Result<(), xous_kernel::Error> { + let process = unsafe { &mut *PROCESS }; + let mut thread = &mut process.threads[tid]; + if thread.sepc >= 4 { + thread.sepc -= 4; + } + Ok(()) + } + /// Initialize this process thread with the given entrypoint and stack /// addresses. pub fn setup_process(pid: PID, thread_init: ThreadInit) -> Result<(), xous_kernel::Error> { diff --git a/kernel/src/main.rs b/kernel/src/main.rs index f671aaeec..0e8032653 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -25,6 +25,8 @@ mod syscall; use services::SystemServices; use xous_kernel::*; + +#[cfg(baremetal)] use utralib::generated::*; #[cfg(baremetal)] diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index 77924eee1..eb0a47eca 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -10,6 +10,25 @@ use xous_kernel::*; /// This is the context that called SwitchTo static mut SWITCHTO_CALLER: Option<(PID, TID)> = None; +fn do_yield(_pid: PID, tid: TID) -> SysCallResult { + // If we're not running on bare metal, treat this as a no-op. + if !cfg!(baremetal) { + return Ok(xous_kernel::Result::Ok); + } + + let (parent_pid, parent_ctx) = unsafe { + SWITCHTO_CALLER + .take() + .expect("yielded when no parent context was present") + }; + SystemServices::with_mut(|ss| { + // TODO: Advance thread + ss.activate_process_thread(tid, parent_pid, parent_ctx, true) + .map(|_| Ok(xous_kernel::Result::ResumeProcess)) + .unwrap_or(Err(xous_kernel::Error::ProcessNotFound)) + }) +} + fn send_message(pid: PID, thread: TID, cid: CID, message: Message) -> SysCallResult { SystemServices::with_mut(|ss| { let sidx = ss @@ -279,7 +298,12 @@ fn return_memory(pid: PID, tid: TID, sender: MessageSender, buf: MemoryRange) -> }) } -fn return_scalar(pid: PID, _tid: TID, sender: MessageSender, arg: usize) -> SysCallResult { +fn return_scalar( + server_pid: PID, + server_tid: TID, + sender: MessageSender, + arg: usize, +) -> SysCallResult { SystemServices::with_mut(|ss| { let sender = SenderID::from(sender); @@ -289,7 +313,7 @@ fn return_scalar(pid: PID, _tid: TID, sender: MessageSender, arg: usize) -> SysC let server = ss .server_from_sidx_mut(sidx) .ok_or(xous_kernel::Error::ServerNotFound)?; - if server.pid != pid { + if server.pid != server_pid { return Err(xous_kernel::Error::ServerNotFound); } let result = server.take_waiting_message(sender.idx, None)?; @@ -318,10 +342,27 @@ fn return_scalar(pid: PID, _tid: TID, sender: MessageSender, arg: usize) -> SysC return Err(xous_kernel::Error::ProcessNotFound); } }; - ss.ready_thread(client_pid, client_tid)?; - ss.switch_to_thread(client_pid, Some(client_tid))?; - ss.set_thread_result(client_pid, client_tid, xous_kernel::Result::Scalar1(arg))?; - Ok(xous_kernel::Result::Scalar1(arg)) + + if !cfg!(baremetal) { + // In a hosted environment, `switch_to_thread()` doesn't continue + // execution from the new thread. Instead it continues in the old + // thread. Therefore, we need to instruct the client to resume, and + // return to the server. + // In a baremetal environment, the opposite is true -- we instruct + // the server to resume and return to the client. + ss.set_thread_result(client_pid, client_tid, xous_kernel::Result::Scalar1(arg))?; + Ok(xous_kernel::Result::Ok) + } else { + // Switch away from the server, but leave it as Runnable + ss.switch_from_thread(server_pid, server_tid)?; + ss.ready_thread(server_pid, server_tid)?; + ss.set_thread_result(server_pid, server_tid, xous_kernel::Result::Ok)?; + + // Switch to the client + ss.ready_thread(client_pid, client_tid)?; + ss.switch_to_thread(client_pid, Some(client_tid))?; + Ok(xous_kernel::Result::Scalar1(arg)) + } }) } @@ -368,22 +409,31 @@ fn return_scalar2( return Err(xous_kernel::Error::ProcessNotFound); } }; - ss.ready_thread(client_pid, client_tid)?; - ss.ready_thread(server_pid, server_tid)?; - /* - ss.activate_process_thread(server_tid, client_pid, client_tid, false) - .map(|_| Ok(xous_kernel::Result::Scalar2(arg1, arg2))) - .unwrap_or(Err(xous_kernel::Error::ProcessNotFound))?;*/ - //ss.switch_from_thread(server_pid, server_tid)?; + if !cfg!(baremetal) { + // In a hosted environment, `switch_to_thread()` doesn't continue + // execution from the new thread. Instead it continues in the old + // thread. Therefore, we need to instruct the client to resume, and + // return to the server. + // In a baremetal environment, the opposite is true -- we instruct + // the server to resume and return to the client. + ss.set_thread_result( + client_pid, + client_tid, + xous_kernel::Result::Scalar2(arg1, arg2), + )?; + Ok(xous_kernel::Result::Ok) + } else { + // Switch away from the server, but leave it as Runnable + ss.switch_from_thread(server_pid, server_tid)?; + ss.ready_thread(server_pid, server_tid)?; + ss.set_thread_result(server_pid, server_tid, xous_kernel::Result::Ok)?; - ss.switch_to_thread(client_pid, Some(client_tid))?; - ss.set_thread_result( - client_pid, - client_tid, - xous_kernel::Result::Scalar2(arg1, arg2), - )?; - Ok(xous_kernel::Result::Scalar2(arg1, arg2)) + // Switch to the client + ss.ready_thread(client_pid, client_tid)?; + ss.switch_to_thread(client_pid, Some(client_tid))?; + Ok(xous_kernel::Result::Scalar2(arg1, arg2)) + } }) } @@ -446,7 +496,12 @@ pub fn handle(pid: PID, tid: TID, call: SysCall) -> SysCallResult { print!("KERNEL({}:{}): Syscall {:x?}", pid, tid, call); let result = handle_inner(pid, tid, call); #[cfg(feature = "debug-print")] - println!(" -> {:x?}", result); + println!( + " -> ({}:{}) {:?}", + crate::arch::current_pid(), + crate::arch::process::Process::current().current_tid(), + result + ); result } @@ -598,24 +653,7 @@ pub fn handle_inner(pid: PID, tid: TID, call: SysCall) -> SysCallResult { interrupt_claim(no, pid as definitions::PID, callback, arg) .map(|_| xous_kernel::Result::Ok) } - SysCall::Yield => { - // If we're not running on bare metal, treat this as a no-op. - if !cfg!(baremetal) { - return Ok(xous_kernel::Result::Ok); - } - - let (parent_pid, parent_ctx) = unsafe { - SWITCHTO_CALLER - .take() - .expect("yielded when no parent context was present") - }; - SystemServices::with_mut(|ss| { - // TODO: Advance thread - ss.activate_process_thread(tid, parent_pid, parent_ctx, true) - .map(|_| Ok(xous_kernel::Result::ResumeProcess)) - .unwrap_or(Err(xous_kernel::Error::ProcessNotFound)) - }) - } + SysCall::Yield => do_yield(pid, tid), SysCall::ReturnToParentI(_pid, _cpuid) => { unsafe { // let (_current_pid, _current_ctx) = crate::arch::irq::take_isr_return_pair() @@ -679,7 +717,17 @@ pub fn handle_inner(pid: PID, tid: TID, call: SysCall) -> SysCallResult { // SysCall::Connect(sid) => { // SystemServices::with_mut(|ss| ss.connect_to_server(sid).map(xous_kernel::Result::ConnectionID)) // } - // SysCall::SendMessage(cid, message) => send_message(pid, tid, cid, message), + SysCall::SendMessage(cid, message) => { + let result = send_message(pid, tid, cid, message); + match result { + Ok(o) => Ok(o), + Err(xous_kernel::Error::ServerQueueFull) => { + arch::process::Process::with_current_mut(|p| p.retry_instruction(tid))?; + do_yield(pid, tid) + } + Err(e) => Err(e), + } + } _ => panic!("Unhandled Syscall: {:?}", call), //Err(xous_kernel::Error::UnhandledSyscall), } } diff --git a/services/graphics-server/src/lib.rs b/services/graphics-server/src/lib.rs index cdcebbdbb..e72b2217b 100644 --- a/services/graphics-server/src/lib.rs +++ b/services/graphics-server/src/lib.rs @@ -3,24 +3,24 @@ pub mod api; pub use api::{Point, Color}; -use xous::{try_send_message, CID}; +use xous::{send_message, CID}; pub fn draw_line(cid: CID, start: Point, end: Point) -> Result<(), xous::Error> { - try_send_message(cid, api::Opcode::Line(start, end).into()).map(|_| ()) + send_message(cid, api::Opcode::Line(start, end).into()).map(|_| ()) } pub fn draw_circle(cid: CID, center: Point, radius: u32) -> Result<(), xous::Error> { - try_send_message(cid, api::Opcode::Circle(center, radius).into()).map(|_| ()) + send_message(cid, api::Opcode::Circle(center, radius).into()).map(|_| ()) } pub fn draw_rectangle(cid: CID, start: Point, end: Point) -> Result<(), xous::Error> { - try_send_message(cid, api::Opcode::Rectangle(start, end).into()).map(|_| ()) + send_message(cid, api::Opcode::Rectangle(start, end).into()).map(|_| ()) } pub fn set_style(cid: CID, width: u32, stroke: Color, fill: Color) -> Result<(), xous::Error> { - try_send_message(cid, api::Opcode::Style(width, stroke, fill).into()).map(|_| ()) + send_message(cid, api::Opcode::Style(width, stroke, fill).into()).map(|_| ()) } pub fn flush(cid: CID) -> Result<(), xous::Error> { - try_send_message(cid, api::Opcode::Flush.into()).map(|_| ()) + send_message(cid, api::Opcode::Flush.into()).map(|_| ()) } diff --git a/xous-rs/src/syscall.rs b/xous-rs/src/syscall.rs index c9c657c9d..81914b443 100644 --- a/xous-rs/src/syscall.rs +++ b/xous-rs/src/syscall.rs @@ -981,21 +981,26 @@ pub fn try_send_message(connection: CID, message: Message) -> core::result::Resu } } -/// Send a message to a server, but keep trying if the server queue is full. +/// Send a message to a server. Depending on the mesage type (move or borrow), it +/// will either block (borrow) or return immediately (move). +/// If the message type is `borrow`, then the memory addresses pointed to will be +/// unavailable to this process until this function returns. +/// +/// If the server queue is full, this will block. /// /// # Errors /// /// * **ServerNotFound**: The server does not exist so the connection is now invalid /// * **BadAddress**: The client tried to pass a Memory message using an address it doesn't own /// * **Timeout**: The timeout limit has been reached -pub fn send_message(connection: CID, message: Message) -> core::result::Result<(), Error> { +pub fn send_message(connection: CID, message: Message) -> core::result::Result { let result = rsyscall(SysCall::SendMessage(connection, message)); - if let Ok(Result::Ok) = result { - Ok(()) - } else if let Err(e) = result { - Err(e) - } else { - panic!("Unexpected return value: {:?}", result); + match result { + Ok(Result::Ok) => Ok(Result::Ok), + Ok(Result::Scalar1(a)) => Ok(Result::Scalar1(a)), + Ok(Result::Scalar2(a, b)) => Ok(Result::Scalar2(a, b)), + Err(e) => Err(e), + v => panic!("Unexpected return value: {:?}", v), } }