pub const MAX_THREAD: TID = 31; use crate::services::ProcessInner; use core::cell::RefCell; use std::io::Write; use std::net::TcpStream; use std::thread_local; use xous_kernel::{ProcessInit, ProcessKey, ThreadInit, PID, TID}; pub const INITIAL_TID: usize = 1; pub const MAX_PROCESS_COUNT: usize = 32; pub struct Process { pid: PID, } #[derive(Debug)] struct ProcessImpl { /// Global parameters used by the operating system pub inner: ProcessInner, /// A 16-byte key used to register a process when it first starts key: ProcessKey, /// The network connection to the client process. conn: Option, /// Memory that may need to be returned to the caller for each thread memory_to_return: [Option>; MAX_THREAD + 1], /// This enables the kernel to keep track of threads in the /// target process, and know which threads are ready to /// receive messages. threads: [Thread; MAX_THREAD + 1], /// The currently-active thread for this process current_thread: TID, } impl PartialEq for Process { fn eq(&self, other: &Process) -> bool { self.pid == other.pid } } struct ProcessTable { /// The process upon which the current syscall is operating current: PID, /// The number of processes that exist total: usize, /// The actual table contents table: Vec>, } thread_local!( static PROCESS_TABLE: RefCell = RefCell::new(ProcessTable { current: unsafe { PID::new_unchecked(1) }, total: 0, table: Vec::new(), }) ); pub fn current_pid() -> PID { PROCESS_TABLE.with(|pt| pt.borrow().current) } pub fn set_current_pid(pid: PID) { PROCESS_TABLE.with(|pt| { let pid_idx = (pid.get() - 1) as usize; let mut pt = pt.borrow_mut(); // // If the PID doesn't exist, only allow it if the table is // // currently empty. // for (idx, i) in pt.table.iter().enumerate() { // println!("pt.table[{}]: {:?}", idx, i); // } match pt.table.get_mut(pid_idx) { None | Some(None) => { // if pid.get() != 1 || pt.total > 0 { panic!("PID {} does not exist", pid); // } } Some(_) => {} } pt.current = pid }); } pub fn register_connection_for_key( conn: TcpStream, key: ProcessKey, ) -> Result { PROCESS_TABLE.with(|pt| { let mut process_table = pt.borrow_mut(); for (pid_minus_1, process) in process_table.table.iter_mut().enumerate() { if let Some(process) = process.as_mut() { if process.key == key && process.conn.is_none() { process.conn = Some(conn); return Ok(PID::new(pid_minus_1 as u8 + 1).unwrap()); } } } Err(xous_kernel::Error::ProcessNotFound) }) } #[repr(C)] #[derive(Copy, Clone, Debug)] /// Everything required to keep track of a single thread of execution. /// In a `std` environment, we can't manage threads so this is a no-op. pub struct Thread { allocated: bool, } impl Default for Thread { fn default() -> Self { Thread { allocated: false } } } // /// Everything required to initialize a process on this platform // pub struct ProcessInit { // /// A network connection to the client // conn: TcpStream, // } // impl ProcessInit { // pub fn new(conn: TcpStream) -> ProcessInit { // ProcessInit { conn } // } // } impl Process { pub fn current() -> Process { let current_pid = PROCESS_TABLE.with(|pt| pt.borrow().current); Process { pid: current_pid } } /// Mark this process as running (on the current core?!) pub fn activate(&mut self) -> Result<(), xous_kernel::Error> { // let mut pt = PROCESS_TABLE.lock().unwrap(); // assert!(pt.table[self.pid as usize - 1] == *self); // pt.current = self.pid as _; Ok(()) } /// Calls the provided function with the current inner process state. pub fn with_inner(f: F) -> R where F: FnOnce(&ProcessInner) -> R, { PROCESS_TABLE.with(|pt| { let process_table = pt.borrow(); let current = &process_table.table[process_table.current.get() as usize - 1] .as_ref() .unwrap(); f(¤t.inner) }) } pub fn with_inner_mut(f: F) -> R where F: FnOnce(&mut ProcessInner) -> R, { PROCESS_TABLE.with(|pt| { let mut process_table = pt.borrow_mut(); let current_pid_idx = process_table.current.get() as usize - 1; let current = &mut process_table.table[current_pid_idx].as_mut().unwrap(); f(&mut current.inner) }) } /// 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(); assert!(!process.threads[thread - 1].allocated); 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) } pub fn setup_thread( &mut self, thread: TID, _setup: ThreadInit, ) -> Result<(), xous_kernel::Error> { // println!( // "KERNEL({}): Setting up thread {} @ {:?}", // self.pid, // thread, // std::thread::current() // ); assert!(thread > 0); PROCESS_TABLE.with(|pt| { let process_table = &mut *pt.borrow_mut(); Self::setup_thread_inner(thread, process_table); // println!( // "KERNEL({}): self.contexts[{}].allocated = {}", // current_pid_idx, // context - 1, // process.contexts[context - 1].allocated // ); }); Ok(()) } // pub fn current_thread(&mut self) -> TID { // PROCESS_TABLE.with(|pt| { // let mut process_table = pt.borrow_mut(); // let current_pid_idx = process_table.current.get() as usize - 1; // let process = &mut process_table.table[current_pid_idx].as_mut().unwrap(); // process.current_context // }) // } /// Set the current context number. pub fn set_thread(&mut self, thread: TID) -> Result<(), xous_kernel::Error> { assert!(thread > 0); PROCESS_TABLE.with(|pt| { let mut process_table = pt.borrow_mut(); let current_pid_idx = process_table.current.get() as usize - 1; let process = &mut process_table.table[current_pid_idx].as_mut().unwrap(); assert!( process.threads[thread - 1].allocated, "tried to switch to thread {} which wasn't allocated", thread ); process.current_thread = thread; }); Ok(()) } #[allow(dead_code)] pub fn find_free_thread(&self) -> Option { PROCESS_TABLE.with(|pt| { let mut process_table = pt.borrow_mut(); let current_pid_idx = process_table.current.get() as usize - 1; let process = &mut process_table.table[current_pid_idx].as_mut().unwrap(); for (index, thread) in process.threads.iter().enumerate() { if !thread.allocated { return Some(index as TID + 1); } } None }) } pub fn set_thread_result(&mut self, tid: TID, result: xous_kernel::Result) { assert!(tid > 0); PROCESS_TABLE.with(|pt| { let mut process_table = pt.borrow_mut(); let current_pid_idx = process_table.current.get() as usize - 1; let process = &mut process_table.table[current_pid_idx].as_mut().unwrap(); assert!( process.threads[tid - 1].allocated, "thread {} is not allocated", tid, ); let mut response = vec![]; // Add the destination thread ID to the start of the packet. response.extend_from_slice(&tid.to_le_bytes()); // Append the contents of the response packet. for word in result.to_args().iter_mut() { response.extend_from_slice(&word.to_le_bytes()); } // If there is memory to return for this thread, also return that. if let Some(buf) = process.memory_to_return[tid - 1].take() { // if let xous_kernel::Result::Message(_) = result { // } else { // panic!( // "memory was waiting to be returned, but message was not a result message" // ); // } response.extend_from_slice(&buf); } // eprintln!( // "KERNEL({}): Setting thread return value to {:?}", // current_pid_idx + 1, // response // ); process .conn .as_mut() .unwrap() .write_all(&response) .expect("Disconnection"); }); } pub fn return_memory(&mut self, tid: TID, buf: &[u8]) { PROCESS_TABLE.with(|pt| { let mut process_table = pt.borrow_mut(); let current_pid_idx = process_table.current.get() as usize - 1; let process = &mut process_table.table[current_pid_idx].as_mut().unwrap(); assert!(process.memory_to_return[tid - 1].is_none()); process.memory_to_return[tid - 1] = Some(buf.to_vec()); }); } /// Initialize this process with the given memory space. THIS DOES NOT /// INITIALIZE A MAIN THREAD. You must call `setup_thread()` in order to /// select a main thread. pub fn create(pid: PID, init_data: ProcessInit) -> PID { PROCESS_TABLE.with(|process_table| { let mut process_table = process_table.borrow_mut(); let pid_idx = (pid.get() - 1) as usize; use crate::filled_array; let process = ProcessImpl { inner: Default::default(), conn: None, key: init_data.key, memory_to_return: filled_array![None; 32 /* MAX_THREAD */], current_thread: INITIAL_TID, threads: [Thread { allocated: false }; MAX_THREAD + 1], }; process_table.total += 1; if pid_idx >= process_table.table.len() { process_table.table.push(Some(process)); } else if process_table.table[pid_idx].is_none() { process_table.table[pid_idx] = Some(process); } else { panic!("pid already allocated!"); } pid }) } pub fn destroy(pid: PID) -> Result<(), xous_kernel::Error> { PROCESS_TABLE.with(|pt| { let mut process_table = pt.borrow_mut(); let pid_idx = pid.get() as usize - 1; if pid_idx >= process_table.table.len() { panic!("attempted to destroy PID that exceeds table index: {}", pid); } let process = process_table.table[pid_idx].as_mut().unwrap(); process .conn .as_mut() .unwrap() .shutdown(std::net::Shutdown::Both) .unwrap(); process_table.table[pid_idx] = None; process_table.total -= 1; Ok(()) }) } pub fn send(&mut self, bytes: &[u8]) -> Result<(), xous_kernel::Error> { // eprintln!("KERNEL: Sending syscall response: {:?}", bytes); PROCESS_TABLE.with(|pt| { let mut process_table = pt.borrow_mut(); let current_pid_idx = process_table.current.get() as usize - 1; let process = &mut process_table.table[current_pid_idx].as_mut().unwrap(); process.conn.as_mut().unwrap().write_all(bytes).unwrap(); }); Ok(()) } } impl Thread {}